From 9bff3742c17b2a4a1e6b82560ab4da5c8c543031 Mon Sep 17 00:00:00 2001 From: Itxaka Date: Thu, 20 Jul 2023 15:53:48 +0200 Subject: [PATCH] Load elemental config/spec from cloud config (#82) --- internal/agent/config.go | 2 +- internal/agent/hooks/runstage.go | 4 +- internal/agent/install.go | 62 ++++------ internal/agent/install_test.go | 26 +--- internal/agent/interactive_install.go | 14 +-- internal/agent/reset.go | 25 ++-- internal/agent/upgrade.go | 19 +-- main.go | 19 ++- pkg/constants/constants.go | 2 +- pkg/elementalConfig/config.go | 168 +++++++++++++------------- pkg/types/v1/config.go | 17 ++- 11 files changed, 150 insertions(+), 208 deletions(-) diff --git a/internal/agent/config.go b/internal/agent/config.go index 535c2f0..65479a3 100644 --- a/internal/agent/config.go +++ b/internal/agent/config.go @@ -31,7 +31,7 @@ type Config struct { func LoadConfig(path ...string) (*Config, error) { if len(path) == 0 { - path = append(path, "/etc/kairos/agent.yaml", "/etc/elemental/config.yaml") + path = append(path, "/etc/kairos/agent.yaml") } cfg := &Config{} diff --git a/internal/agent/hooks/runstage.go b/internal/agent/hooks/runstage.go index 6c2708b..4a0e551 100644 --- a/internal/agent/hooks/runstage.go +++ b/internal/agent/hooks/runstage.go @@ -10,8 +10,8 @@ import ( type RunStage struct{} -func (r RunStage) Run(_ config.Config) error { - cfg, err := elementalConfig.ReadConfigRun("/etc/elemental") +func (r RunStage) Run(c config.Config) error { + cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(&c) if err != nil { cfg.Logger.Errorf("Error reading config: %s\n", err) } diff --git a/internal/agent/install.go b/internal/agent/install.go index ffc52a0..ccbd5fa 100644 --- a/internal/agent/install.go +++ b/internal/agent/install.go @@ -11,10 +11,6 @@ import ( "syscall" "time" - events "github.com/kairos-io/kairos-sdk/bus" - "github.com/kairos-io/kairos-sdk/collector" - "github.com/kairos-io/kairos-sdk/machine" - "github.com/kairos-io/kairos-sdk/utils" hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks" "github.com/kairos-io/kairos-agent/v2/internal/bus" "github.com/kairos-io/kairos-agent/v2/internal/cmd" @@ -23,6 +19,10 @@ import ( "github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig" v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" elementalUtils "github.com/kairos-io/kairos-agent/v2/pkg/utils" + events "github.com/kairos-io/kairos-sdk/bus" + "github.com/kairos-io/kairos-sdk/collector" + "github.com/kairos-io/kairos-sdk/machine" + "github.com/kairos-io/kairos-sdk/utils" qr "github.com/mudler/go-nodepair/qrcode" "github.com/mudler/go-pluggable" "github.com/pterm/pterm" @@ -96,14 +96,7 @@ func ManualInstall(c string, options map[string]string, strictValidations bool) } } } - - // Load the installation Config from the system - installConfig, err := elementalConfig.ReadConfigRun("/etc/elemental") - if err != nil { - return err - } - - return RunInstall(installConfig, options) + return RunInstall(options) } func Install(dir ...string) error { @@ -130,12 +123,6 @@ func Install(dir ...string) error { ensureDataSourceReady() - // Load the installation Config from the system - installConfig, err := elementalConfig.ReadConfigRun("/etc/elemental") - if err != nil { - return err - } - // Reads config, and if present and offline is defined, // runs the installation cc, err := config.Scan(collector.Directories(dir...), collector.MergeBootLine, collector.NoLogs) @@ -148,7 +135,7 @@ func Install(dir ...string) error { r["device"] = cc.Install.Device mergeOption(configStr, r) - err = RunInstall(installConfig, r) + err = RunInstall(r) if err != nil { return err } @@ -242,7 +229,7 @@ func Install(dir ...string) error { pterm.Info.Println("Starting installation") - if err := RunInstall(installConfig, r); err != nil { + if err := RunInstall(r); err != nil { return err } @@ -259,14 +246,7 @@ func Install(dir ...string) error { return nil } -func RunInstall(installConfig *v1.RunConfig, options map[string]string) error { - f, err := elementalUtils.TempFile(installConfig.Fs, "", "kairos-install-config-xxx.yaml") - if err != nil { - installConfig.Logger.Error("Error creating temporal file for install config: %s\n", err.Error()) - return err - } - defer os.RemoveAll(f.Name()) - +func RunInstall(options map[string]string) error { cloudInit, ok := options["cc"] if !ok { fmt.Println("cloudInit must be specified among options") @@ -286,12 +266,6 @@ func RunInstall(installConfig *v1.RunConfig, options map[string]string) error { env := append(c.Install.Env, c.Env...) utils.SetEnv(env) - err = os.WriteFile(f.Name(), []byte(cloudInit), os.ModePerm) - if err != nil { - fmt.Printf("could not write cloud init to %s: %s\n", f.Name(), err.Error()) - return err - } - _, reboot := options["reboot"] _, poweroff := options["poweroff"] if poweroff { @@ -301,8 +275,24 @@ func RunInstall(installConfig *v1.RunConfig, options map[string]string) error { c.Install.Reboot = true } - // Generate the installation spec - installSpec, _ := elementalConfig.ReadInstallSpec(installConfig) + // Load the installation Config from the system + installConfig, installSpec, err := elementalConfig.ReadInstallConfigFromAgentConfig(c) + if err != nil { + return err + } + + f, err := elementalUtils.TempFile(installConfig.Fs, "", "kairos-install-config-xxx.yaml") + if err != nil { + installConfig.Logger.Error("Error creating temporal file for install config: %s\n", err.Error()) + return err + } + defer os.RemoveAll(f.Name()) + + err = os.WriteFile(f.Name(), []byte(cloudInit), os.ModePerm) + if err != nil { + fmt.Printf("could not write cloud init to %s: %s\n", f.Name(), err.Error()) + return err + } installSpec.NoFormat = c.Install.NoFormat diff --git a/internal/agent/install_test.go b/internal/agent/install_test.go index 41bdab9..dd5ad63 100644 --- a/internal/agent/install_test.go +++ b/internal/agent/install_test.go @@ -1,18 +1,15 @@ package agent import ( - "bytes" "context" "fmt" "github.com/jaypipes/ghw/pkg/block" "github.com/kairos-io/kairos-agent/v2/pkg/constants" - conf "github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig" "github.com/kairos-io/kairos-agent/v2/pkg/utils" "os" "path/filepath" "github.com/kairos-io/kairos-agent/v2/pkg/config" - v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks" "github.com/twpayne/go-vfs" "github.com/twpayne/go-vfs/vfst" @@ -66,27 +63,17 @@ var _ = Describe("prepareConfiguration", func() { }) var _ = Describe("RunInstall", func() { - var installConfig *v1.RunConfig var options map[string]string var err error var fs vfs.FS - var cloudInit *v1mock.FakeCloudInitRunner var cleanup func() - var memLog *bytes.Buffer var ghwTest v1mock.GhwMock var cmdline func() ([]byte, error) BeforeEach(func() { // Default mock objects runner := v1mock.NewFakeRunner() - syscall := &v1mock.FakeSyscall{} - mounter := v1mock.NewErrorMounter() - memLog = &bytes.Buffer{} - logger := v1.NewBufferLogger(memLog) - logger = v1.NewLogger() - extractor := v1mock.NewFakeImageExtractor(logger) //logger.SetLevel(v1.DebugLevel()) - cloudInit = &v1mock.FakeCloudInitRunner{} // Set default cmdline function so we dont panic :o cmdline = func() ([]byte, error) { return []byte{}, nil @@ -105,17 +92,6 @@ var _ = Describe("RunInstall", func() { _, err = fs.Create(grubCfg) Expect(err).To(BeNil()) - // Create new runconfig with all mocked objects - installConfig = conf.NewRunConfig( - conf.WithFs(fs), - conf.WithRunner(runner), - conf.WithLogger(logger), - conf.WithMounter(mounter), - conf.WithSyscall(syscall), - conf.WithCloudInitRunner(cloudInit), - conf.WithImageExtractor(extractor), - ) - // Side effect of runners, hijack calls to commands and return our stuff partNum := 0 partedOut := printOutput @@ -225,7 +201,7 @@ install: It("runs the install", func() { Skip("Not ready yet") - err = RunInstall(installConfig, options) + err = RunInstall(options) Expect(err).ToNot(HaveOccurred()) }) }) diff --git a/internal/agent/interactive_install.go b/internal/agent/interactive_install.go index d1a82bb..0cd0002 100644 --- a/internal/agent/interactive_install.go +++ b/internal/agent/interactive_install.go @@ -8,8 +8,6 @@ import ( "github.com/kairos-io/kairos-agent/v2/internal/bus" "github.com/kairos-io/kairos-agent/v2/internal/cmd" "github.com/kairos-io/kairos-agent/v2/pkg/config" - "github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig" - events "github.com/kairos-io/kairos-sdk/bus" "github.com/kairos-io/kairos-sdk/unstructured" @@ -19,7 +17,6 @@ import ( "github.com/mudler/go-pluggable" "github.com/mudler/yip/pkg/schema" "github.com/pterm/pterm" - "github.com/spf13/viper" ) const ( @@ -277,16 +274,7 @@ func InteractiveInstall(debug, spawnShell bool) error { pterm.Info.Println("Starting installation") pterm.Info.Println(finalCloudConfig) - // Set debug from here already, so it's loaded by the ReadConfigRun - viper.Set("debug", debug) - - // Load the installation Config from the system - installConfig, err := elementalConfig.ReadConfigRun("/etc/elemental") - if err != nil { - return err - } - - err = RunInstall(installConfig, map[string]string{ + err = RunInstall(map[string]string{ "device": device, "cc": finalCloudConfig, }) diff --git a/internal/agent/reset.go b/internal/agent/reset.go index 40bf622..6ad9938 100644 --- a/internal/agent/reset.go +++ b/internal/agent/reset.go @@ -3,28 +3,26 @@ package agent import ( "encoding/json" "fmt" - "github.com/sanity-io/litter" - "github.com/sirupsen/logrus" "os" "sync" "time" - sdk "github.com/kairos-io/kairos-sdk/bus" - "github.com/kairos-io/kairos-sdk/collector" - "github.com/kairos-io/kairos-sdk/machine" - "github.com/kairos-io/kairos-sdk/utils" hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks" "github.com/kairos-io/kairos-agent/v2/internal/bus" "github.com/kairos-io/kairos-agent/v2/internal/cmd" "github.com/kairos-io/kairos-agent/v2/pkg/action" "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig" + sdk "github.com/kairos-io/kairos-sdk/bus" + "github.com/kairos-io/kairos-sdk/collector" + "github.com/kairos-io/kairos-sdk/machine" + "github.com/kairos-io/kairos-sdk/utils" "github.com/mudler/go-pluggable" "github.com/pterm/pterm" ) -func Reset(debug bool, dir ...string) error { +func Reset(dir ...string) error { // TODO: Enable args? No args for now so no possibility of reset persistent or overriding the source for the reset // Nor the auto-reboot via cmd? // This comment pertains calling reset via cmdline when wanting to override configs @@ -43,7 +41,6 @@ func Reset(debug bool, dir ...string) error { // This loads yet another config ¬_¬ // TODO: merge this somehow with the rest so there is no 5 places to configure stuff? - // Also this reads the elemental config.yaml agentConfig, err := LoadConfig() if err != nil { return err @@ -83,18 +80,12 @@ func Reset(debug bool, dir ...string) error { utils.SetEnv(c.Env) - resetConfig, err := elementalConfig.ReadConfigRun("/etc/elemental") - if err != nil { - return err - } - if debug { - resetConfig.Logger.SetLevel(logrus.DebugLevel) - } - resetConfig.Logger.Debugf("Full config: %s\n", litter.Sdump(resetConfig)) - resetSpec, err := elementalConfig.ReadResetSpec(resetConfig) + // Load the installation Config from the cloud-config data + resetConfig, resetSpec, err := elementalConfig.ReadResetConfigFromAgentConfig(c) if err != nil { return err } + // Not even sure what opts can come from here to be honest. Where is the struct that supports this options? // Where is the docs to support this? This is generic af and not easily identifiable if len(options) == 0 { diff --git a/internal/agent/upgrade.go b/internal/agent/upgrade.go index 9ef4da6..32bfe7c 100644 --- a/internal/agent/upgrade.go +++ b/internal/agent/upgrade.go @@ -17,8 +17,6 @@ import ( "github.com/kairos-io/kairos-sdk/collector" "github.com/kairos-io/kairos-sdk/utils" "github.com/mudler/go-pluggable" - "github.com/sanity-io/litter" - log "github.com/sirupsen/logrus" ) func ListReleases(includePrereleases bool) semver.Collection { @@ -52,7 +50,7 @@ func ListReleases(includePrereleases bool) semver.Collection { } func Upgrade( - version, source string, force, debug, strictValidations bool, dirs []string, preReleases bool) error { + version, source string, force, strictValidations bool, dirs []string, preReleases bool) error { bus.Manager.Initialize() if version == "" && source == "" { @@ -93,10 +91,6 @@ func Upgrade( } } - if debug { - fmt.Printf("Upgrading to source: '%s'\n", img) - } - c, err := config.Scan(collector.Directories(dirs...), collector.StrictValidation(strictValidations)) if err != nil { return err @@ -105,18 +99,11 @@ func Upgrade( utils.SetEnv(c.Env) // Load the upgrade Config from the system - upgradeConfig, err := elementalConfig.ReadConfigRun("/etc/elemental") + upgradeConfig, upgradeSpec, err := elementalConfig.ReadUpgradeConfigFromAgentConfig(c) if err != nil { return err } - if debug { - upgradeConfig.Logger.SetLevel(log.DebugLevel) - } - upgradeConfig.Logger.Debugf("Full config: %s\n", litter.Sdump(upgradeConfig)) - - // Generate the upgrade spec - upgradeSpec, _ := elementalConfig.ReadUpgradeSpec(upgradeConfig) // Add the image source imgSource, err := v1.NewSrcFromURI(img) if err != nil { @@ -124,7 +111,7 @@ func Upgrade( } upgradeSpec.Active.Source = imgSource - // Sanitize (this is not required but good to do + // Sanitize err = upgradeSpec.Sanitize() if err != nil { return err diff --git a/main.go b/main.go index f8675e1..857a09c 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/kairos-io/kairos-agent/v2/pkg/utils" "os" "path/filepath" "regexp" @@ -17,7 +18,6 @@ import ( "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig" v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" - "github.com/kairos-io/kairos-agent/v2/pkg/utils" "github.com/kairos-io/kairos-sdk/bundles" "github.com/kairos-io/kairos-sdk/collector" "github.com/kairos-io/kairos-sdk/machine" @@ -136,7 +136,7 @@ See https://kairos.io/docs/upgrade/manual/ for documentation. } return agent.Upgrade( - v, source, c.Bool("force"), c.Bool("debug"), + v, source, c.Bool("force"), c.Bool("strict-validation"), configScanDir, c.Bool("pre"), ) @@ -457,7 +457,8 @@ This command is meant to be used from the boot GRUB menu, but can likely be used { Name: "reset", Action: func(c *cli.Context) error { - return agent.Reset(c.Bool("debug"), configScanDir...) + + return agent.Reset(configScanDir...) }, Usage: "Starts kairos reset mode", Description: ` @@ -535,7 +536,11 @@ The validate command expects a configuration file as its only argument. Local fi }, Action: func(c *cli.Context) error { stage := c.Args().First() - cfg, err := elementalConfig.ReadConfigRun("/etc/elemental") + config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs) + if err != nil { + return err + } + cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(config) cfg.Strict = c.Bool("strict") if len(c.StringSlice("cloud-init-paths")) > 0 { @@ -582,7 +587,11 @@ The validate command expects a configuration file as its only argument. Local fi if err != nil { return fmt.Errorf("invalid path %s", destination) } - cfg, err := elementalConfig.ReadConfigRun("/etc/elemental") + config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs) + if err != nil { + return err + } + cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(config) if err != nil { return err } diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index c130d1b..bb0fc21 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -24,7 +24,7 @@ import ( const ( GrubConf = "/etc/cos/grub.cfg" GrubOEMEnv = "grub_oem_env" - GrubDefEntry = "cOS" + GrubDefEntry = "Kairos" DefaultTty = "tty1" BiosPartName = "bios" EfiLabel = "COS_GRUB" diff --git a/pkg/elementalConfig/config.go b/pkg/elementalConfig/config.go index dd4d977..c8ec5fd 100644 --- a/pkg/elementalConfig/config.go +++ b/pkg/elementalConfig/config.go @@ -18,6 +18,7 @@ package elementalConfig import ( "fmt" + "gopkg.in/yaml.v3" "io" "io/fs" "os" @@ -28,6 +29,7 @@ import ( "github.com/kairos-io/kairos-agent/v2/internal/common" "github.com/kairos-io/kairos-agent/v2/pkg/cloudinit" + agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/constants" "github.com/kairos-io/kairos-agent/v2/pkg/http" v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" @@ -35,7 +37,6 @@ import ( "github.com/mitchellh/mapstructure" "github.com/sanity-io/litter" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/twpayne/go-vfs" "k8s.io/mount-utils" @@ -222,11 +223,13 @@ func NewInstallSpec(cfg v1.Config) *v1.InstallSpec { recoveryImg.Source = v1.NewFileSrc(recoveryImgFile) recoveryImg.FS = constants.SquashFs recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoverySquashFile) + recoveryImg.Size = constants.ImgSize } else { recoveryImg.Source = v1.NewFileSrc(activeImg.File) recoveryImg.FS = constants.LinuxImgFs recoveryImg.Label = constants.SystemLabel recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoveryImgFile) + recoveryImg.Size = constants.ImgSize } passiveImg = v1.Image{ @@ -234,6 +237,7 @@ func NewInstallSpec(cfg v1.Config) *v1.InstallSpec { Label: constants.PassiveLabel, Source: v1.NewFileSrc(activeImg.File), FS: constants.LinuxImgFs, + Size: constants.ImgSize, } return &v1.InstallSpec{ @@ -364,6 +368,7 @@ func NewUpgradeSpec(cfg v1.Config) (*v1.UpgradeSpec, error) { passive = v1.Image{ File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile), Label: constants.PassiveLabel, + Size: constants.ImgSize, Source: v1.NewFileSrc(active.File), FS: active.FS, } @@ -507,6 +512,7 @@ func NewResetSpec(cfg v1.Config) (*v1.ResetSpec, error) { Passive: v1.Image{ File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile), Label: constants.PassiveLabel, + Size: constants.ImgSize, Source: v1.NewFileSrc(activeFile), FS: constants.LinuxImgFs, }, @@ -544,112 +550,102 @@ func NewBuildConfig(opts ...GenericOptions) *v1.BuildConfig { return b } -func ReadConfigRun(configDir string) (*v1.RunConfig, error) { +// ReadConfigRunFromAgentConfig reads the configuration directly from a given cloud config string +func ReadConfigRunFromAgentConfig(c *agentConfig.Config) (*v1.RunConfig, error) { cfg := NewRunConfig(WithLogger(v1.NewLogger()), WithOCIImageExtractor()) + var err error + + cc, err := c.String() + if err != nil { + return nil, err + } configLogger(cfg.Logger, cfg.Fs) - - // TODO: is this really needed? It feels quite wrong, shouldn't it be loaded - // as regular environment variables? - // IMHO loading os-release as env variables should be sufficient here - cfgDefault := []string{"/etc/os-release"} - for _, c := range cfgDefault { - if exists, _ := utils.Exists(cfg.Fs, c); exists { - viper.SetConfigFile(c) - viper.SetConfigType("env") - cobra.CheckErr(viper.MergeInConfig()) - } - } - - // merge yaml config files on top of default runconfig - if exists, _ := utils.Exists(cfg.Fs, configDir); exists { - viper.AddConfigPath(configDir) - viper.SetConfigType("yaml") - viper.SetConfigName("config") - // If a config file is found, read it in. - err := viper.MergeInConfig() - if err != nil { - cfg.Logger.Warnf("error merging config files: %s", err) - } - } - - // Load extra config files on configdir/config.d/ so we can override config values - cfgExtra := fmt.Sprintf("%s/config.d/", strings.TrimSuffix(configDir, "/")) - if exists, _ := utils.Exists(cfg.Fs, cfgExtra); exists { - viper.AddConfigPath(cfgExtra) - _ = filepath.WalkDir(cfgExtra, func(path string, d fs.DirEntry, err error) error { - if !d.IsDir() && filepath.Ext(d.Name()) == ".yaml" { - viper.SetConfigType("yaml") - viper.SetConfigName(strings.TrimSuffix(d.Name(), ".yaml")) - cobra.CheckErr(viper.MergeInConfig()) - } - return nil - }) - } - - // unmarshal all the vars into the RunConfig object - err := viper.Unmarshal(cfg, setDecoder, decodeHook) + err = yaml.Unmarshal([]byte(cc), &cfg) if err != nil { - cfg.Logger.Warnf("error unmarshalling RunConfig: %s", err) + return nil, err } - + // Store the full cloud-config in here so we can reuse it afterwards + cfg.FullCloudConfig = cc err = cfg.Sanitize() cfg.Logger.Debugf("Full config loaded: %s", litter.Sdump(cfg)) return cfg, err } -func ReadInstallSpec(r *v1.RunConfig) (*v1.InstallSpec, error) { - install := NewInstallSpec(r.Config) - vp := viper.Sub("install") - if vp == nil { - vp = viper.New() - } +// readSpecFromCloudConfig returns a v1.Spec for the given spec +func readSpecFromCloudConfig(r *v1.RunConfig, spec string) (v1.Spec, error) { + var sp v1.Spec + var err error - err := vp.Unmarshal(install, setDecoder, decodeHook) - if err != nil { - r.Logger.Warnf("error unmarshalling InstallSpec: %s", err) + switch spec { + case "install": + sp = NewInstallSpec(r.Config) + case "upgrade": + sp, err = NewUpgradeSpec(r.Config) + case "reset": + sp, err = NewResetSpec(r.Config) + default: + return nil, fmt.Errorf("spec not valid: %s", spec) } - err = install.Sanitize() - r.Logger.Debugf("Loaded install spec: %s", litter.Sdump(install)) - return install, err -} - -func ReadUpgradeSpec(r *v1.RunConfig) (*v1.UpgradeSpec, error) { - upgrade, err := NewUpgradeSpec(r.Config) - if err != nil { - return nil, fmt.Errorf("failed initializing upgrade spec: %v", err) - } - vp := viper.Sub("upgrade") - if vp == nil { - vp = viper.New() - } - - err = vp.Unmarshal(upgrade, setDecoder, decodeHook) - if err != nil { - r.Logger.Warnf("error unmarshalling UpgradeSpec: %s", err) - } - err = upgrade.Sanitize() - r.Logger.Debugf("Loaded upgrade UpgradeSpec: %s", litter.Sdump(upgrade)) - return upgrade, err -} - -func ReadResetSpec(r *v1.RunConfig) (*v1.ResetSpec, error) { - reset, err := NewResetSpec(r.Config) if err != nil { return nil, fmt.Errorf("failed initializing reset spec: %v", err) } - vp := viper.Sub("reset") + + // Load the config into viper from the raw cloud config string + viper.SetConfigType("yaml") + viper.ReadConfig(strings.NewReader(r.FullCloudConfig)) + vp := viper.Sub(spec) if vp == nil { vp = viper.New() } - err = vp.Unmarshal(reset, setDecoder, decodeHook) + err = vp.Unmarshal(sp, setDecoder, decodeHook) if err != nil { - r.Logger.Warnf("error unmarshalling ResetSpec: %s", err) + r.Logger.Warnf("error unmarshalling %s Spec: %s", spec, err) } - err = reset.Sanitize() - r.Logger.Debugf("Loaded reset spec: %s", litter.Sdump(reset)) - return reset, err + r.Logger.Debugf("Loaded %s spec: %s", litter.Sdump(sp)) + return sp, err +} + +// readConfigAndSpecFromAgentConfig will return the config and spec for the given action based off the agent Config +func readConfigAndSpecFromAgentConfig(c *agentConfig.Config, action string) (*v1.RunConfig, v1.Spec, error) { + runConfig, err := ReadConfigRunFromAgentConfig(c) + if err != nil { + return nil, nil, err + } + spec, err := readSpecFromCloudConfig(runConfig, action) + if err != nil { + return nil, nil, err + } + return runConfig, spec, nil +} + +// ReadResetConfigFromAgentConfig will return a proper v1.RunConfig and v1.ResetSpec based on an agent Config +func ReadResetConfigFromAgentConfig(c *agentConfig.Config) (*v1.RunConfig, *v1.ResetSpec, error) { + config, spec, err := readConfigAndSpecFromAgentConfig(c, "reset") + if err != nil { + return nil, nil, err + } + resetSpec := spec.(*v1.ResetSpec) + return config, resetSpec, nil +} + +func ReadInstallConfigFromAgentConfig(c *agentConfig.Config) (*v1.RunConfig, *v1.InstallSpec, error) { + config, spec, err := readConfigAndSpecFromAgentConfig(c, "install") + if err != nil { + return nil, nil, err + } + installSpec := spec.(*v1.InstallSpec) + return config, installSpec, nil +} + +func ReadUpgradeConfigFromAgentConfig(c *agentConfig.Config) (*v1.RunConfig, *v1.UpgradeSpec, error) { + config, spec, err := readConfigAndSpecFromAgentConfig(c, "upgrade") + if err != nil { + return nil, nil, err + } + upgradeSpec := spec.(*v1.UpgradeSpec) + return config, upgradeSpec, nil } func configLogger(log v1.Logger, vfs v1.FS) { diff --git a/pkg/types/v1/config.go b/pkg/types/v1/config.go index 9ced774..b538531 100644 --- a/pkg/types/v1/config.go +++ b/pkg/types/v1/config.go @@ -122,12 +122,13 @@ func (c *Config) Sanitize() error { } type RunConfig struct { - Debug bool `yaml:"strict,omitempty" mapstructure:"debug"` - Strict bool `yaml:"strict,omitempty" mapstructure:"strict"` - Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` - PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` - CloudInitPaths []string `yaml:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths"` - EjectCD bool `yaml:"eject-cd,omitempty" mapstructure:"eject-cd"` + Debug bool `yaml:"debug,omitempty" mapstructure:"debug"` + Strict bool `yaml:"strict,omitempty" mapstructure:"strict"` + Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` + PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` + CloudInitPaths []string `yaml:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths"` + EjectCD bool `yaml:"eject-cd,omitempty" mapstructure:"eject-cd"` + FullCloudConfig string // Stores the full cloud config used to generate the spec afterwards // 'inline' and 'squash' labels ensure config fields // are embedded from a yaml and map PoV @@ -197,6 +198,10 @@ func (i *InstallSpec) Sanitize() error { return i.Partitions.SetFirmwarePartitions(i.Firmware, i.PartTable) } +type Spec interface { + Sanitize() error +} + // ResetSpec struct represents all the reset action details type ResetSpec struct { FormatPersistent bool `yaml:"reset-persistent,omitempty" mapstructure:"reset-persistent"`