2023-05-26 10:32:01 +00:00
package schema
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 schema of the Kairos configuration together.
type RootSchema struct {
2023-07-25 10:38:46 +00:00
_ 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" `
Debug bool ` json:"debug,omitempty" mapstructure:"debug" `
Strict bool ` json:"strict,omitempty" mapstructure:"strict" `
CloudInitPaths [ ] string ` json:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths" `
EjectCD bool ` json:"eject-cd,omitempty" mapstructure:"eject-cd" `
FullCloudConfig string ` json:"fullcloudconfig,omitempty" mapstructure:"fullcloudconfig" `
Cosign bool ` json:"cosign,omitempty" mapstructure:"cosign" `
Verify bool ` json:"verify,omitempty" mapstructure:"verify" `
CosignPubKey string ` json:"cosign-key,omitempty" mapstructure:"cosign-key" `
Arch string ` json:"arch,omitempty" mapstructure:"arch" `
Platform PlatformSchema ` json:"platform,omitempty" mapstructure:"platform" `
SquashFsCompressionConfig [ ] string ` json:"squash-compression,omitempty" mapstructure:"squash-compression" `
SquashFsNoCompression bool ` json:"squash-no-compression,omitempty" mapstructure:"squash-no-compression" `
2023-12-18 14:39:09 +00:00
UkiMaxEntries int ` json:"uki-max-entries,omitempty" mapstructure:"uki-max-entries" `
2023-07-25 10:38:46 +00:00
}
type PlatformSchema struct {
OS string
Arch string
GolangArch string
2023-05-26 10:32:01 +00:00
}
// KConfig is used to parse and validate Kairos configuration files.
type KConfig struct {
Source string
parsed interface { }
ValidationError error
schemaType interface { }
}
// 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 ) {
reflector := jsonschemago . Reflector { }
generatedSchema , err := reflector . Reflect ( schemaType )
if err != nil {
return "" , err
}
if url != "" {
generatedSchema . WithSchema ( url )
}
generatedSchemaJSON , err := json . MarshalIndent ( generatedSchema , "" , " " )
if err != nil {
return "" , err
}
return string ( generatedSchemaJSON ) , nil
}
func ( kc * KConfig ) validate ( ) {
generatedSchemaJSON , err := GenerateSchema ( kc . schemaType , "" )
if err != nil {
kc . ValidationError = err
return
}
sch , err := jsonschema . CompileString ( "schema.json" , string ( generatedSchemaJSON ) )
if err != nil {
kc . ValidationError = err
return
}
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
}
// HasHeader returns true if the config has one of the valid headers.
func ( kc * KConfig ) HasHeader ( ) bool {
var found bool
availableHeaders := [ ] string { "#cloud-config" , "#kairos-config" , "#node-config" }
for _ , header := range availableHeaders {
if strings . HasPrefix ( kc . Source , header ) {
found = true
}
}
return found
}
// 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 string , st interface { } ) ( * KConfig , error ) {
kc := & KConfig {
Source : s ,
schemaType : st ,
}
err := yaml . Unmarshal ( [ ] byte ( s ) , & kc . parsed )
if err != nil {
return kc , err
}
return kc , nil
}