mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-08-07 03:14:01 +00:00
99 lines
2.8 KiB
Go
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
|
||
|
}
|