kairos-agent/pkg/config/schemas/root_schema.go
Mauro Morales 84c68ff0b8 seedling: Kairos config validator library (#798)
* Validate yaml

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* lint feedback

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Validate User name with JsonSchema

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* WIP users validation

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add multiple examples for ssh keys

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add example of complex validation with AnyOf

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Better business rule example with P2P

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Test with message for empty network_token

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Split into a file for each sub section

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add install schema validations

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add to main schema

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add more tests for p2p

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add install schema

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Validate fields between new and old schema

It also adds the missing ones

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Lint

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Remove temp debugging functions

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add new fields in old schema

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add documentation for all exported

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Move schemas into a directory of their own

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Add missing dot at end of comment

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

* Rebase master and add local_file to bundles

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>

---------

Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-02-08 11:02:13 +01:00

99 lines
2.8 KiB
Go

package config
import (
"encoding/json"
"fmt"
"strings"
"github.com/santhosh-tekuri/jsonschema/v5"
jsonschemago "github.com/swaggest/jsonschema-go"
"gopkg.in/yaml.v3"
)
// RootSchema groups all the different schemas of the Kairos configuration together.
type RootSchema struct {
_ struct{} `title:"Kairos Schema" description:"Defines all valid Kairos configuration attributes."`
Bundles []BundleSchema `json:"bundles,omitempty" description:"Add bundles in runtime"`
ConfigURL string `json:"config_url,omitempty" description:"URL download configuration from."`
Env []string `json:"env,omitempty"`
FailOnBundleErrors bool `json:"fail_on_bundles_errors,omitempty"`
GrubOptionsSchema `json:"grub_options,omitempty"`
Install InstallSchema `json:"install,omitempty"`
Options []interface{} `json:"options,omitempty" description:"Various options."`
Users []UserSchema `json:"users,omitempty" minItems:"1" required:"true"`
P2P P2PSchema `json:"p2p,omitempty"`
}
// KConfig is used to parse and validate Kairos configuration files.
type KConfig struct {
source string
parsed interface{}
validationError error
schemaType interface{}
header string
}
func (kc *KConfig) validate() {
reflector := jsonschemago.Reflector{}
generatedSchema, err := reflector.Reflect(kc.schemaType)
if err != nil {
kc.validationError = err
}
generatedSchemaJSON, err := json.MarshalIndent(generatedSchema, "", " ")
if err != nil {
kc.validationError = err
}
sch, err := jsonschema.CompileString("schema.json", string(generatedSchemaJSON))
if err != nil {
kc.validationError = err
}
if err = sch.Validate(kc.parsed); err != nil {
kc.validationError = err
}
}
// IsValid returns true if the schema rules of the configuration are valid.
func (kc *KConfig) IsValid() bool {
kc.validate()
return kc.validationError == nil
}
// ValidationError returns one of the errors of an invalid schemam rule, when the configuration is valid, then it returns an empty string.
func (kc *KConfig) ValidationError() string {
kc.validate()
if kc.validationError != nil {
return kc.validationError.Error()
}
return ""
}
func (kc *KConfig) hasHeader() bool {
return strings.HasPrefix(kc.source, kc.header)
}
// NewConfigFromYAML is a constructor for KConfig instances. The source of the configuration is passed in YAML and if there are any issues unmarshaling it will return an error.
func NewConfigFromYAML(s, h string, st interface{}) (*KConfig, error) {
kc := &KConfig{
source: s,
header: h,
schemaType: st,
}
if !kc.hasHeader() {
return kc, fmt.Errorf("missing %s header", kc.header)
}
err := yaml.Unmarshal([]byte(s), &kc.parsed)
if err != nil {
return kc, err
}
return kc, nil
}