2023-02-08 10:02:13 +00:00
package config
import (
"encoding/json"
"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 {
2023-02-14 15:15:13 +00:00
Source string
2023-02-08 10:02:13 +00:00
parsed interface { }
2023-02-14 15:15:13 +00:00
ValidationError error
2023-02-08 10:02:13 +00:00
schemaType interface { }
}
2023-02-14 15:15:13 +00:00
// GenerateSchema takes the given schema type and builds a JSON Schema out of it
// if a URL is passed it will also add it as the $schema key, which is useful when
// defining a version of a Root Schema which will be available online.
func GenerateSchema ( schemaType interface { } , url string ) ( string , error ) {
2023-02-08 10:02:13 +00:00
reflector := jsonschemago . Reflector { }
2023-02-14 15:15:13 +00:00
generatedSchema , err := reflector . Reflect ( schemaType )
2023-02-08 10:02:13 +00:00
if err != nil {
2023-02-14 15:15:13 +00:00
return "" , err
}
if url != "" {
generatedSchema . WithSchema ( url )
2023-02-08 10:02:13 +00:00
}
generatedSchemaJSON , err := json . MarshalIndent ( generatedSchema , "" , " " )
if err != nil {
2023-02-14 15:15:13 +00:00
return "" , err
}
return string ( generatedSchemaJSON ) , nil
}
func ( kc * KConfig ) validate ( ) {
generatedSchemaJSON , err := GenerateSchema ( kc . schemaType , "" )
if err != nil {
kc . ValidationError = err
return
2023-02-08 10:02:13 +00:00
}
sch , err := jsonschema . CompileString ( "schema.json" , string ( generatedSchemaJSON ) )
if err != nil {
2023-02-14 15:15:13 +00:00
kc . ValidationError = err
return
2023-02-08 10:02:13 +00:00
}
if err = sch . Validate ( kc . parsed ) ; err != nil {
2023-02-14 15:15:13 +00:00
kc . ValidationError = err
2023-02-08 10:02:13 +00:00
}
}
// IsValid returns true if the schema rules of the configuration are valid.
func ( kc * KConfig ) IsValid ( ) bool {
kc . validate ( )
2023-02-14 15:15:13 +00:00
return kc . ValidationError == nil
2023-02-08 10:02:13 +00:00
}
2023-02-14 15:15:13 +00:00
// HasHeader returns true if the config has one of the valid headers.
func ( kc * KConfig ) HasHeader ( ) bool {
var found bool
2023-02-08 10:02:13 +00:00
2023-02-14 15:15:13 +00:00
availableHeaders := [ ] string { "#cloud-config" , "#kairos-config" , "#node-config" }
for _ , header := range availableHeaders {
if strings . HasPrefix ( kc . Source , header ) {
found = true
}
2023-02-08 10:02:13 +00:00
}
2023-02-14 15:15:13 +00:00
return found
2023-02-08 10:02:13 +00:00
}
// 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.
2023-02-14 15:15:13 +00:00
func NewConfigFromYAML ( s string , st interface { } ) ( * KConfig , error ) {
2023-02-08 10:02:13 +00:00
kc := & KConfig {
2023-02-14 15:15:13 +00:00
Source : s ,
2023-02-08 10:02:13 +00:00
schemaType : st ,
}
err := yaml . Unmarshal ( [ ] byte ( s ) , & kc . parsed )
if err != nil {
return kc , err
}
return kc , nil
}