Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(plan): add plan section support (#488)
The plan library was originally written as a configuration schema for the services manager (servstate). Over time the need arose to support configurations for other managers such as checks (checkstate) and log-targets (logstate). The configuration schema for these managers, closely related to the services manager, has since also been built in to the plan library. The services manager and its related checks and log-targets functionality will always be part of the Pebble core. However, as Pebble is getting more functionality (additional managers) and also used as a core in derivative projects, a more modular and dynamic approach to extending the schema is needed. Add an the ```SectionExtension``` interface for use by managers who wishes to register a schema extension during Pebble startup. Inside each layer, top level entries are now referred to as ```sections``` (built-in sections includes ```summary```, ```description```, ```services```, ```log-targets``` and ```checks```). Each section has an associated ```field``` that is the top level key, and if supplied by an extension, an opaque backing type ```Section```. **SectionExtension interface:** ``` // SectionExtension allows the plan layer schema to be extended without // adding centralised schema knowledge to the plan library. type SectionExtension interface { // ParseSection returns a newly allocated concrete type containing the // unmarshalled section content. ParseSection(data yaml.Node) (LayerSection, error) // CombineSections returns a newly allocated concrete type containing the // result of combining the supplied sections in order. CombineSections(sections ...LayerSection) (LayerSection, error) // ValidatePlan takes the complete plan as input, and allows the // extension to validate the plan. This can be used for cross section // dependency validation. ValidatePlan(plan *Plan) error } type Section interface { // Validate checks whether the section is valid, returning an error if not. Validate() error // IsZero reports whether the section is empty. IsZero() bool } ``` **Example usage:** ``` // New SectionExtension type type fooExtension struct{} func (f *fooExtension) ParseSection(data yaml.Node) (LayerSection, error) {...} func (f *fooExtension) CombineSections(sections ...LayerSection) (LayerSection, error) {...} func (f *fooExtension) ValidatePlan(plan *Plan) error {...} // New Section type type FooSection struct { Entries map[string]Bar `yaml:",inline,omitempty"` } type Bar struct { Name string `yaml:"name,omitempty"` } func (s *FooSection) Validate() error {...} func (s *FooSection) IsZero() bool {...} ``` ``` // Early startup plan.RegisterExtension("foo", &fooExtension{}) : // Load layers containing new section newPlan := plan.ReadDir(layersDir) : // Show plan yaml.Marshal(newPlan) ``` Example YAML output: ``` foo: bar1: name: test1 bar2: name: test2 ```
- Loading branch information