mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-08-17 15:57:46 +00:00
Load elemental config/spec from cloud config (#82)
This commit is contained in:
parent
fd22840d14
commit
9bff3742c1
@ -31,7 +31,7 @@ type Config struct {
|
|||||||
|
|
||||||
func LoadConfig(path ...string) (*Config, error) {
|
func LoadConfig(path ...string) (*Config, error) {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path = append(path, "/etc/kairos/agent.yaml", "/etc/elemental/config.yaml")
|
path = append(path, "/etc/kairos/agent.yaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &Config{}
|
cfg := &Config{}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
type RunStage struct{}
|
type RunStage struct{}
|
||||||
|
|
||||||
func (r RunStage) Run(_ config.Config) error {
|
func (r RunStage) Run(c config.Config) error {
|
||||||
cfg, err := elementalConfig.ReadConfigRun("/etc/elemental")
|
cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(&c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cfg.Logger.Errorf("Error reading config: %s\n", err)
|
cfg.Logger.Errorf("Error reading config: %s\n", err)
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"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"
|
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/bus"
|
||||||
"github.com/kairos-io/kairos-agent/v2/internal/cmd"
|
"github.com/kairos-io/kairos-agent/v2/internal/cmd"
|
||||||
@ -23,6 +19,10 @@ import (
|
|||||||
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
|
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
|
||||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||||
elementalUtils "github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
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"
|
qr "github.com/mudler/go-nodepair/qrcode"
|
||||||
"github.com/mudler/go-pluggable"
|
"github.com/mudler/go-pluggable"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
@ -96,14 +96,7 @@ func ManualInstall(c string, options map[string]string, strictValidations bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return RunInstall(options)
|
||||||
// Load the installation Config from the system
|
|
||||||
installConfig, err := elementalConfig.ReadConfigRun("/etc/elemental")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return RunInstall(installConfig, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Install(dir ...string) error {
|
func Install(dir ...string) error {
|
||||||
@ -130,12 +123,6 @@ func Install(dir ...string) error {
|
|||||||
|
|
||||||
ensureDataSourceReady()
|
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,
|
// Reads config, and if present and offline is defined,
|
||||||
// runs the installation
|
// runs the installation
|
||||||
cc, err := config.Scan(collector.Directories(dir...), collector.MergeBootLine, collector.NoLogs)
|
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
|
r["device"] = cc.Install.Device
|
||||||
mergeOption(configStr, r)
|
mergeOption(configStr, r)
|
||||||
|
|
||||||
err = RunInstall(installConfig, r)
|
err = RunInstall(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -242,7 +229,7 @@ func Install(dir ...string) error {
|
|||||||
|
|
||||||
pterm.Info.Println("Starting installation")
|
pterm.Info.Println("Starting installation")
|
||||||
|
|
||||||
if err := RunInstall(installConfig, r); err != nil {
|
if err := RunInstall(r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,14 +246,7 @@ func Install(dir ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunInstall(installConfig *v1.RunConfig, options map[string]string) error {
|
func RunInstall(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())
|
|
||||||
|
|
||||||
cloudInit, ok := options["cc"]
|
cloudInit, ok := options["cc"]
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Println("cloudInit must be specified among options")
|
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...)
|
env := append(c.Install.Env, c.Env...)
|
||||||
utils.SetEnv(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"]
|
_, reboot := options["reboot"]
|
||||||
_, poweroff := options["poweroff"]
|
_, poweroff := options["poweroff"]
|
||||||
if poweroff {
|
if poweroff {
|
||||||
@ -301,8 +275,24 @@ func RunInstall(installConfig *v1.RunConfig, options map[string]string) error {
|
|||||||
c.Install.Reboot = true
|
c.Install.Reboot = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the installation spec
|
// Load the installation Config from the system
|
||||||
installSpec, _ := elementalConfig.ReadInstallSpec(installConfig)
|
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
|
installSpec.NoFormat = c.Install.NoFormat
|
||||||
|
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/jaypipes/ghw/pkg/block"
|
"github.com/jaypipes/ghw/pkg/block"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
"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"
|
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
"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"
|
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
|
||||||
"github.com/twpayne/go-vfs"
|
"github.com/twpayne/go-vfs"
|
||||||
"github.com/twpayne/go-vfs/vfst"
|
"github.com/twpayne/go-vfs/vfst"
|
||||||
@ -66,27 +63,17 @@ var _ = Describe("prepareConfiguration", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
var _ = Describe("RunInstall", func() {
|
var _ = Describe("RunInstall", func() {
|
||||||
var installConfig *v1.RunConfig
|
|
||||||
var options map[string]string
|
var options map[string]string
|
||||||
var err error
|
var err error
|
||||||
var fs vfs.FS
|
var fs vfs.FS
|
||||||
var cloudInit *v1mock.FakeCloudInitRunner
|
|
||||||
var cleanup func()
|
var cleanup func()
|
||||||
var memLog *bytes.Buffer
|
|
||||||
var ghwTest v1mock.GhwMock
|
var ghwTest v1mock.GhwMock
|
||||||
var cmdline func() ([]byte, error)
|
var cmdline func() ([]byte, error)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
// Default mock objects
|
// Default mock objects
|
||||||
runner := v1mock.NewFakeRunner()
|
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())
|
//logger.SetLevel(v1.DebugLevel())
|
||||||
cloudInit = &v1mock.FakeCloudInitRunner{}
|
|
||||||
// Set default cmdline function so we dont panic :o
|
// Set default cmdline function so we dont panic :o
|
||||||
cmdline = func() ([]byte, error) {
|
cmdline = func() ([]byte, error) {
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
@ -105,17 +92,6 @@ var _ = Describe("RunInstall", func() {
|
|||||||
_, err = fs.Create(grubCfg)
|
_, err = fs.Create(grubCfg)
|
||||||
Expect(err).To(BeNil())
|
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
|
// Side effect of runners, hijack calls to commands and return our stuff
|
||||||
partNum := 0
|
partNum := 0
|
||||||
partedOut := printOutput
|
partedOut := printOutput
|
||||||
@ -225,7 +201,7 @@ install:
|
|||||||
|
|
||||||
It("runs the install", func() {
|
It("runs the install", func() {
|
||||||
Skip("Not ready yet")
|
Skip("Not ready yet")
|
||||||
err = RunInstall(installConfig, options)
|
err = RunInstall(options)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/kairos-io/kairos-agent/v2/internal/bus"
|
"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/internal/cmd"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
"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"
|
events "github.com/kairos-io/kairos-sdk/bus"
|
||||||
"github.com/kairos-io/kairos-sdk/unstructured"
|
"github.com/kairos-io/kairos-sdk/unstructured"
|
||||||
|
|
||||||
@ -19,7 +17,6 @@ import (
|
|||||||
"github.com/mudler/go-pluggable"
|
"github.com/mudler/go-pluggable"
|
||||||
"github.com/mudler/yip/pkg/schema"
|
"github.com/mudler/yip/pkg/schema"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -277,16 +274,7 @@ func InteractiveInstall(debug, spawnShell bool) error {
|
|||||||
pterm.Info.Println("Starting installation")
|
pterm.Info.Println("Starting installation")
|
||||||
pterm.Info.Println(finalCloudConfig)
|
pterm.Info.Println(finalCloudConfig)
|
||||||
|
|
||||||
// Set debug from here already, so it's loaded by the ReadConfigRun
|
err = RunInstall(map[string]string{
|
||||||
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{
|
|
||||||
"device": device,
|
"device": device,
|
||||||
"cc": finalCloudConfig,
|
"cc": finalCloudConfig,
|
||||||
})
|
})
|
||||||
|
@ -3,28 +3,26 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sanity-io/litter"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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"
|
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/bus"
|
||||||
"github.com/kairos-io/kairos-agent/v2/internal/cmd"
|
"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/action"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
|
"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/mudler/go-pluggable"
|
||||||
"github.com/pterm/pterm"
|
"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
|
// 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?
|
// Nor the auto-reboot via cmd?
|
||||||
// This comment pertains calling reset via cmdline when wanting to override configs
|
// 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 ¬_¬
|
// This loads yet another config ¬_¬
|
||||||
// TODO: merge this somehow with the rest so there is no 5 places to configure stuff?
|
// 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()
|
agentConfig, err := LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -83,18 +80,12 @@ func Reset(debug bool, dir ...string) error {
|
|||||||
|
|
||||||
utils.SetEnv(c.Env)
|
utils.SetEnv(c.Env)
|
||||||
|
|
||||||
resetConfig, err := elementalConfig.ReadConfigRun("/etc/elemental")
|
// Load the installation Config from the cloud-config data
|
||||||
if err != nil {
|
resetConfig, resetSpec, err := elementalConfig.ReadResetConfigFromAgentConfig(c)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if debug {
|
|
||||||
resetConfig.Logger.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
resetConfig.Logger.Debugf("Full config: %s\n", litter.Sdump(resetConfig))
|
|
||||||
resetSpec, err := elementalConfig.ReadResetSpec(resetConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not even sure what opts can come from here to be honest. Where is the struct that supports this options?
|
// 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
|
// Where is the docs to support this? This is generic af and not easily identifiable
|
||||||
if len(options) == 0 {
|
if len(options) == 0 {
|
||||||
|
@ -17,8 +17,6 @@ import (
|
|||||||
"github.com/kairos-io/kairos-sdk/collector"
|
"github.com/kairos-io/kairos-sdk/collector"
|
||||||
"github.com/kairos-io/kairos-sdk/utils"
|
"github.com/kairos-io/kairos-sdk/utils"
|
||||||
"github.com/mudler/go-pluggable"
|
"github.com/mudler/go-pluggable"
|
||||||
"github.com/sanity-io/litter"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListReleases(includePrereleases bool) semver.Collection {
|
func ListReleases(includePrereleases bool) semver.Collection {
|
||||||
@ -52,7 +50,7 @@ func ListReleases(includePrereleases bool) semver.Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Upgrade(
|
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()
|
bus.Manager.Initialize()
|
||||||
|
|
||||||
if version == "" && source == "" {
|
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))
|
c, err := config.Scan(collector.Directories(dirs...), collector.StrictValidation(strictValidations))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -105,18 +99,11 @@ func Upgrade(
|
|||||||
utils.SetEnv(c.Env)
|
utils.SetEnv(c.Env)
|
||||||
|
|
||||||
// Load the upgrade Config from the system
|
// Load the upgrade Config from the system
|
||||||
upgradeConfig, err := elementalConfig.ReadConfigRun("/etc/elemental")
|
upgradeConfig, upgradeSpec, err := elementalConfig.ReadUpgradeConfigFromAgentConfig(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Add the image source
|
||||||
imgSource, err := v1.NewSrcFromURI(img)
|
imgSource, err := v1.NewSrcFromURI(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -124,7 +111,7 @@ func Upgrade(
|
|||||||
}
|
}
|
||||||
upgradeSpec.Active.Source = imgSource
|
upgradeSpec.Active.Source = imgSource
|
||||||
|
|
||||||
// Sanitize (this is not required but good to do
|
// Sanitize
|
||||||
err = upgradeSpec.Sanitize()
|
err = upgradeSpec.Sanitize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
19
main.go
19
main.go
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -17,7 +18,6 @@ import (
|
|||||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
|
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
|
||||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
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/bundles"
|
||||||
"github.com/kairos-io/kairos-sdk/collector"
|
"github.com/kairos-io/kairos-sdk/collector"
|
||||||
"github.com/kairos-io/kairos-sdk/machine"
|
"github.com/kairos-io/kairos-sdk/machine"
|
||||||
@ -136,7 +136,7 @@ See https://kairos.io/docs/upgrade/manual/ for documentation.
|
|||||||
}
|
}
|
||||||
|
|
||||||
return agent.Upgrade(
|
return agent.Upgrade(
|
||||||
v, source, c.Bool("force"), c.Bool("debug"),
|
v, source, c.Bool("force"),
|
||||||
c.Bool("strict-validation"), configScanDir,
|
c.Bool("strict-validation"), configScanDir,
|
||||||
c.Bool("pre"),
|
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",
|
Name: "reset",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
return agent.Reset(c.Bool("debug"), configScanDir...)
|
|
||||||
|
return agent.Reset(configScanDir...)
|
||||||
},
|
},
|
||||||
Usage: "Starts kairos reset mode",
|
Usage: "Starts kairos reset mode",
|
||||||
Description: `
|
Description: `
|
||||||
@ -535,7 +536,11 @@ The validate command expects a configuration file as its only argument. Local fi
|
|||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
stage := c.Args().First()
|
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")
|
cfg.Strict = c.Bool("strict")
|
||||||
|
|
||||||
if len(c.StringSlice("cloud-init-paths")) > 0 {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid path %s", destination)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
GrubConf = "/etc/cos/grub.cfg"
|
GrubConf = "/etc/cos/grub.cfg"
|
||||||
GrubOEMEnv = "grub_oem_env"
|
GrubOEMEnv = "grub_oem_env"
|
||||||
GrubDefEntry = "cOS"
|
GrubDefEntry = "Kairos"
|
||||||
DefaultTty = "tty1"
|
DefaultTty = "tty1"
|
||||||
BiosPartName = "bios"
|
BiosPartName = "bios"
|
||||||
EfiLabel = "COS_GRUB"
|
EfiLabel = "COS_GRUB"
|
||||||
|
@ -18,6 +18,7 @@ package elementalConfig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
@ -28,6 +29,7 @@ import (
|
|||||||
|
|
||||||
"github.com/kairos-io/kairos-agent/v2/internal/common"
|
"github.com/kairos-io/kairos-agent/v2/internal/common"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/cloudinit"
|
"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/constants"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/http"
|
"github.com/kairos-io/kairos-agent/v2/pkg/http"
|
||||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||||
@ -35,7 +37,6 @@ import (
|
|||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/sanity-io/litter"
|
"github.com/sanity-io/litter"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/twpayne/go-vfs"
|
"github.com/twpayne/go-vfs"
|
||||||
"k8s.io/mount-utils"
|
"k8s.io/mount-utils"
|
||||||
@ -222,11 +223,13 @@ func NewInstallSpec(cfg v1.Config) *v1.InstallSpec {
|
|||||||
recoveryImg.Source = v1.NewFileSrc(recoveryImgFile)
|
recoveryImg.Source = v1.NewFileSrc(recoveryImgFile)
|
||||||
recoveryImg.FS = constants.SquashFs
|
recoveryImg.FS = constants.SquashFs
|
||||||
recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoverySquashFile)
|
recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoverySquashFile)
|
||||||
|
recoveryImg.Size = constants.ImgSize
|
||||||
} else {
|
} else {
|
||||||
recoveryImg.Source = v1.NewFileSrc(activeImg.File)
|
recoveryImg.Source = v1.NewFileSrc(activeImg.File)
|
||||||
recoveryImg.FS = constants.LinuxImgFs
|
recoveryImg.FS = constants.LinuxImgFs
|
||||||
recoveryImg.Label = constants.SystemLabel
|
recoveryImg.Label = constants.SystemLabel
|
||||||
recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoveryImgFile)
|
recoveryImg.File = filepath.Join(constants.RecoveryDir, "cOS", constants.RecoveryImgFile)
|
||||||
|
recoveryImg.Size = constants.ImgSize
|
||||||
}
|
}
|
||||||
|
|
||||||
passiveImg = v1.Image{
|
passiveImg = v1.Image{
|
||||||
@ -234,6 +237,7 @@ func NewInstallSpec(cfg v1.Config) *v1.InstallSpec {
|
|||||||
Label: constants.PassiveLabel,
|
Label: constants.PassiveLabel,
|
||||||
Source: v1.NewFileSrc(activeImg.File),
|
Source: v1.NewFileSrc(activeImg.File),
|
||||||
FS: constants.LinuxImgFs,
|
FS: constants.LinuxImgFs,
|
||||||
|
Size: constants.ImgSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &v1.InstallSpec{
|
return &v1.InstallSpec{
|
||||||
@ -364,6 +368,7 @@ func NewUpgradeSpec(cfg v1.Config) (*v1.UpgradeSpec, error) {
|
|||||||
passive = v1.Image{
|
passive = v1.Image{
|
||||||
File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile),
|
File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile),
|
||||||
Label: constants.PassiveLabel,
|
Label: constants.PassiveLabel,
|
||||||
|
Size: constants.ImgSize,
|
||||||
Source: v1.NewFileSrc(active.File),
|
Source: v1.NewFileSrc(active.File),
|
||||||
FS: active.FS,
|
FS: active.FS,
|
||||||
}
|
}
|
||||||
@ -507,6 +512,7 @@ func NewResetSpec(cfg v1.Config) (*v1.ResetSpec, error) {
|
|||||||
Passive: v1.Image{
|
Passive: v1.Image{
|
||||||
File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile),
|
File: filepath.Join(ep.State.MountPoint, "cOS", constants.PassiveImgFile),
|
||||||
Label: constants.PassiveLabel,
|
Label: constants.PassiveLabel,
|
||||||
|
Size: constants.ImgSize,
|
||||||
Source: v1.NewFileSrc(activeFile),
|
Source: v1.NewFileSrc(activeFile),
|
||||||
FS: constants.LinuxImgFs,
|
FS: constants.LinuxImgFs,
|
||||||
},
|
},
|
||||||
@ -544,112 +550,102 @@ func NewBuildConfig(opts ...GenericOptions) *v1.BuildConfig {
|
|||||||
return b
|
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())
|
cfg := NewRunConfig(WithLogger(v1.NewLogger()), WithOCIImageExtractor())
|
||||||
|
var err error
|
||||||
|
|
||||||
|
cc, err := c.String()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
configLogger(cfg.Logger, cfg.Fs)
|
configLogger(cfg.Logger, cfg.Fs)
|
||||||
|
err = yaml.Unmarshal([]byte(cc), &cfg)
|
||||||
// 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 {
|
if err != nil {
|
||||||
cfg.Logger.Warnf("error merging config files: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
// Store the full cloud-config in here so we can reuse it afterwards
|
||||||
|
cfg.FullCloudConfig = cc
|
||||||
// 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)
|
|
||||||
if err != nil {
|
|
||||||
cfg.Logger.Warnf("error unmarshalling RunConfig: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cfg.Sanitize()
|
err = cfg.Sanitize()
|
||||||
cfg.Logger.Debugf("Full config loaded: %s", litter.Sdump(cfg))
|
cfg.Logger.Debugf("Full config loaded: %s", litter.Sdump(cfg))
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadInstallSpec(r *v1.RunConfig) (*v1.InstallSpec, error) {
|
// readSpecFromCloudConfig returns a v1.Spec for the given spec
|
||||||
install := NewInstallSpec(r.Config)
|
func readSpecFromCloudConfig(r *v1.RunConfig, spec string) (v1.Spec, error) {
|
||||||
vp := viper.Sub("install")
|
var sp v1.Spec
|
||||||
if vp == nil {
|
var err error
|
||||||
vp = viper.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
err := vp.Unmarshal(install, setDecoder, decodeHook)
|
switch spec {
|
||||||
if err != nil {
|
case "install":
|
||||||
r.Logger.Warnf("error unmarshalling InstallSpec: %s", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed initializing reset spec: %v", err)
|
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 {
|
if vp == nil {
|
||||||
vp = viper.New()
|
vp = viper.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = vp.Unmarshal(reset, setDecoder, decodeHook)
|
err = vp.Unmarshal(sp, setDecoder, decodeHook)
|
||||||
if err != nil {
|
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 %s spec: %s", litter.Sdump(sp))
|
||||||
r.Logger.Debugf("Loaded reset spec: %s", litter.Sdump(reset))
|
return sp, err
|
||||||
return reset, 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) {
|
func configLogger(log v1.Logger, vfs v1.FS) {
|
||||||
|
@ -122,12 +122,13 @@ func (c *Config) Sanitize() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RunConfig struct {
|
type RunConfig struct {
|
||||||
Debug bool `yaml:"strict,omitempty" mapstructure:"debug"`
|
Debug bool `yaml:"debug,omitempty" mapstructure:"debug"`
|
||||||
Strict bool `yaml:"strict,omitempty" mapstructure:"strict"`
|
Strict bool `yaml:"strict,omitempty" mapstructure:"strict"`
|
||||||
Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"`
|
Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"`
|
||||||
PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"`
|
PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"`
|
||||||
CloudInitPaths []string `yaml:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths"`
|
CloudInitPaths []string `yaml:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths"`
|
||||||
EjectCD bool `yaml:"eject-cd,omitempty" mapstructure:"eject-cd"`
|
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
|
// 'inline' and 'squash' labels ensure config fields
|
||||||
// are embedded from a yaml and map PoV
|
// 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)
|
return i.Partitions.SetFirmwarePartitions(i.Firmware, i.PartTable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Spec interface {
|
||||||
|
Sanitize() error
|
||||||
|
}
|
||||||
|
|
||||||
// ResetSpec struct represents all the reset action details
|
// ResetSpec struct represents all the reset action details
|
||||||
type ResetSpec struct {
|
type ResetSpec struct {
|
||||||
FormatPersistent bool `yaml:"reset-persistent,omitempty" mapstructure:"reset-persistent"`
|
FormatPersistent bool `yaml:"reset-persistent,omitempty" mapstructure:"reset-persistent"`
|
||||||
|
Loading…
Reference in New Issue
Block a user