Merge elemental config into agent config (#102)

This commit is contained in:
Itxaka 2023-07-25 15:21:34 +02:00 committed by GitHub
parent 5b945303c9
commit f7bdba2dda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 799 additions and 1092 deletions

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/jaypipes/ghw v0.12.0
github.com/joho/godotenv v1.5.1
github.com/kairos-io/kairos-sdk v0.0.9
github.com/kairos-io/kairos-sdk v0.0.10
github.com/labstack/echo/v4 v4.10.2
github.com/mitchellh/mapstructure v1.5.0
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb

2
go.sum
View File

@ -358,6 +358,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kairos-io/kairos-sdk v0.0.9 h1:DnuvmnCTDBQg0nr3siaXb5pspi+GUsf1rPb0MFOTdTc=
github.com/kairos-io/kairos-sdk v0.0.9/go.mod h1:Z+1CLqMZq97bzwX2XSIArr8EoniMth3mMYkOOb8L3QY=
github.com/kairos-io/kairos-sdk v0.0.10 h1:TUgrGSGP6Z1CPfA4gjmbb+cCaJg1OR18c+LD+ZRGqMk=
github.com/kairos-io/kairos-sdk v0.0.10/go.mod h1:AK9poWisuhnzri04diXnTG8L7EkOSUArSeeDn2LYFU0=
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
github.com/kendru/darwin/go/depgraph v0.0.0-20221105232959-877d6a81060c h1:eKb4PqwAMhlqwXw0W3atpKaYaPGlXE/Fwh+xpCEYaPk=

View File

@ -10,7 +10,6 @@ type Interface interface {
}
var AfterInstall = []Interface{
&RunStage{}, // Shells out to stages defined from the container image
&GrubOptions{}, // Set custom GRUB options
&BundleOption{},
&CustomMounts{},

View File

@ -1,22 +0,0 @@
package hook
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"
events "github.com/kairos-io/kairos-sdk/bus"
)
type RunStage struct{}
func (r RunStage) Run(c config.Config, _ v1.Spec) error {
cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(&c)
if err != nil {
cfg.Logger.Errorf("Error reading config: %s\n", err)
}
_ = utils.RunStage(cfg, "kairos-install.after")
events.RunHookScript("/usr/bin/kairos-agent.install.after.hook") //nolint:errcheck
return nil
}

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"net/url"
"os"
"strings"
@ -16,7 +17,6 @@ import (
"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"
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"
@ -51,17 +51,6 @@ func displayInfo(agentConfig *Config) {
}
}
func mergeOption(cloudConfig string, r map[string]string) {
c := &config.Config{}
yaml.Unmarshal([]byte(cloudConfig), c) //nolint:errcheck
for k, v := range c.Options {
if k == "cc" {
continue
}
r[k] = v
}
}
func ManualInstall(c, device string, reboot, poweroff, strictValidations bool) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -223,22 +212,21 @@ func RunInstall(c *config.Config) error {
c.Install.Device = detectDevice()
}
// Load the installation Config from the system
installConfig, installSpec, err := elementalConfig.ReadInstallConfigFromAgentConfig(c)
// Load the installation spec from the Config
installSpec, err := config.ReadInstallSpecFromConfig(c)
if err != nil {
return err
}
f, err := elementalUtils.TempFile(installConfig.Fs, "", "kairos-install-config-xxx.yaml")
f, err := fsutils.TempFile(c.Fs, "", "kairos-install-config-xxx.yaml")
if err != nil {
installConfig.Logger.Error("Error creating temporal file for install config: %s\n", err.Error())
c.Logger.Error("Error creating temporary file for install config: %s\n", err.Error())
return err
}
defer os.RemoveAll(f.Name())
ccstring, err := c.String()
if err != nil {
installConfig.Logger.Error("Error creating temporary file for install config: %s\n", err.Error())
return err
}
err = os.WriteFile(f.Name(), []byte(ccstring), os.ModePerm)
@ -247,6 +235,7 @@ func RunInstall(c *config.Config) error {
return err
}
// TODO: This should not be neccessary
installSpec.NoFormat = c.Install.NoFormat
// Set our cloud-init to the file we just created
@ -267,18 +256,20 @@ func RunInstall(c *config.Config) error {
}
// Add user's cloud-config (to run user defined "before-install" stages)
installConfig.CloudInitPaths = append(installConfig.CloudInitPaths, installSpec.CloudInit...)
c.CloudInitPaths = append(c.CloudInitPaths, installSpec.CloudInit...)
// Run pre-install stage
_ = elementalUtils.RunStage(installConfig, "kairos-install.pre")
_ = elementalUtils.RunStage(c, "kairos-install.pre")
events.RunHookScript("/usr/bin/kairos-agent.install.pre.hook") //nolint:errcheck
// Create the action
installAction := action.NewInstallAction(installConfig, installSpec)
installAction := action.NewInstallAction(c, installSpec)
// Run it
if err := installAction.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
_ = elementalUtils.RunStage(c, "kairos-install.after")
events.RunHookScript("/usr/bin/kairos-agent.install.after.hook") //nolint:errcheck
return hook.Run(*c, installSpec, hook.AfterInstall...)
}

View File

@ -5,11 +5,11 @@ import (
"fmt"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
"os"
"path/filepath"
"github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
"github.com/twpayne/go-vfs"
"github.com/twpayne/go-vfs/vfst"
@ -84,10 +84,10 @@ var _ = Describe("RunInstall", func() {
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{"/proc/cmdline": ""})
Expect(err).Should(BeNil())
// Create tmp dir
utils.MkdirAll(fs, "/tmp", constants.DirPerm)
fsutils.MkdirAll(fs, "/tmp", constants.DirPerm)
// Create grub confg
grubCfg := filepath.Join(constants.ActiveDir, constants.GrubConf)
err = utils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(grubCfg)
Expect(err).To(BeNil())
@ -133,7 +133,7 @@ var _ = Describe("RunInstall", func() {
}
device := "/some/device"
err = utils.MkdirAll(fs, filepath.Dir(device), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(device), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(device)
Expect(err).ShouldNot(HaveOccurred())

View File

@ -12,7 +12,6 @@ import (
"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"
@ -78,7 +77,7 @@ func Reset(dir ...string) error {
utils.SetEnv(c.Env)
// Load the installation Config from the cloud-config data
resetConfig, resetSpec, err := elementalConfig.ReadResetConfigFromAgentConfig(c)
resetSpec, err := config.ReadResetSpecFromConfig(c)
if err != nil {
return err
}
@ -92,11 +91,11 @@ func Reset(dir ...string) error {
resetSpec.FormatOEM = o == "true"
}
if s := optionsFromEvent["strict"]; s != "" {
resetConfig.Strict = s == "true"
c.Strict = s == "true"
}
}
resetAction := action.NewResetAction(resetConfig, resetSpec)
resetAction := action.NewResetAction(c, resetSpec)
if err := resetAction.Run(); err != nil {
fmt.Println(err)
os.Exit(1)

View File

@ -11,7 +11,6 @@ import (
"github.com/kairos-io/kairos-agent/v2/internal/bus"
"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"
"github.com/kairos-io/kairos-agent/v2/pkg/github"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
events "github.com/kairos-io/kairos-sdk/bus"
@ -100,7 +99,7 @@ func Upgrade(
utils.SetEnv(c.Env)
// Load the upgrade Config from the system
upgradeConfig, upgradeSpec, err := elementalConfig.ReadUpgradeConfigFromAgentConfig(c)
upgradeSpec, err := config.ReadUpgradeSpecFromConfig(c)
if err != nil {
return err
}
@ -118,7 +117,7 @@ func Upgrade(
return err
}
upgradeAction := action.NewUpgradeAction(upgradeConfig, upgradeSpec)
upgradeAction := action.NewUpgradeAction(c, upgradeSpec)
err = upgradeAction.Run()
if err != nil {

37
main.go
View File

@ -15,8 +15,7 @@ import (
"github.com/kairos-io/kairos-agent/v2/internal/bus"
"github.com/kairos-io/kairos-agent/v2/internal/common"
"github.com/kairos-io/kairos-agent/v2/internal/webui"
"github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-sdk/bundles"
"github.com/kairos-io/kairos-sdk/collector"
@ -279,7 +278,7 @@ E.g. kairos-agent install-bundle container:quay.io/kairos/kairos...
Description: "Show the runtime configuration of the machine. It will scan the machine for all the configuration and will return the config file processed and found.",
Aliases: []string{"s"},
Action: func(c *cli.Context) error {
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs)
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs)
if err != nil {
return err
}
@ -308,7 +307,7 @@ enabled: true`,
Description: "It allows to navigate the YAML config file by searching with 'yq' style keywords as `config get k3s` to retrieve the k3s config block",
Aliases: []string{"g"},
Action: func(c *cli.Context) error {
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation")))
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation")))
if err != nil {
return err
}
@ -527,24 +526,20 @@ The validate command expects a configuration file as its only argument. Local fi
},
Action: func(c *cli.Context) error {
stage := c.Args().First()
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs)
if err != nil {
return err
}
cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(config)
cfg.Strict = c.Bool("strict")
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs)
config.Strict = c.Bool("strict")
if len(c.StringSlice("cloud-init-paths")) > 0 {
cfg.CloudInitPaths = append(cfg.CloudInitPaths, c.StringSlice("cloud-init-paths")...)
config.CloudInitPaths = append(config.CloudInitPaths, c.StringSlice("cloud-init-paths")...)
}
if c.Bool("debug") {
cfg.Logger.SetLevel(logrus.DebugLevel)
config.Logger.SetLevel(logrus.DebugLevel)
}
if err != nil {
cfg.Logger.Errorf("Error reading config: %s\n", err)
config.Logger.Errorf("Error reading config: %s\n", err)
}
return utils.RunStage(cfg, stage)
return utils.RunStage(config, stage)
},
},
{
@ -578,24 +573,16 @@ The validate command expects a configuration file as its only argument. Local fi
if err != nil {
return fmt.Errorf("invalid path %s", destination)
}
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs)
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs)
if err != nil {
return err
}
cfg, err := elementalConfig.ReadConfigRunFromAgentConfig(config)
if err != nil {
return err
}
if c.Bool("debug") {
cfg.Logger.SetLevel(logrus.DebugLevel)
}
cfg.Logger.Infof("Starting download and extraction for image %s to %s\n", image, destination)
config.Logger.Infof("Starting download and extraction for image %s to %s\n", image, destination)
e := v1.OCIImageExtractor{}
if err = e.ExtractImage(image, destination, c.String("platform")); err != nil {
return err
}
cfg.Logger.Infof("Image %s downloaded and extracted to %s correctly\n", image, destination)
config.Logger.Infof("Image %s downloaded and extracted to %s correctly\n", image, destination)
return nil
},
},

View File

@ -17,13 +17,13 @@ limitations under the License.
package action
import (
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
)
// Hook is RunStage wrapper that only adds logic to ignore errors
// in case v1.Config.Strict is set to false
func Hook(config *v1.Config, hook string) error {
func Hook(config *config.Config, hook string) error {
config.Logger.Infof("Running %s hook", hook)
err := utils.RunStage(config, hook)
if !config.Strict {
@ -33,7 +33,7 @@ func Hook(config *v1.Config, hook string) error {
}
// ChrootHook executes Hook inside a chroot environment
func ChrootHook(config *v1.Config, hook string, chrootDir string, bindMounts map[string]string) (err error) {
func ChrootHook(config *config.Config, hook string, chrootDir string, bindMounts map[string]string) (err error) {
callback := func() error {
return Hook(config, hook)
}

View File

@ -18,7 +18,9 @@ package action
import (
"fmt"
"github.com/kairos-io/kairos-agent/v2/pkg/config"
"path/filepath"
"strings"
"time"
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
@ -107,11 +109,11 @@ func (i *InstallAction) createInstallStateYaml(sysMeta, recMeta interface{}) err
}
type InstallAction struct {
cfg *v1.Config
cfg *config.Config
spec *v1.InstallSpec
}
func NewInstallAction(cfg *v1.Config, spec *v1.InstallSpec) *InstallAction {
func NewInstallAction(cfg *config.Config, spec *v1.InstallSpec) *InstallAction {
return &InstallAction{cfg: cfg, spec: spec}
}
@ -259,7 +261,10 @@ func (i InstallAction) Run() (err error) {
}
// If we want to eject the cd, create the required executable so the cd is ejected at shutdown
if i.cfg.EjectCD && utils.BootedFrom(i.cfg.Runner, "cdroot") {
out, _ := i.cfg.Runner.Run("cat", "/proc/cmdline")
bootedFromCD := strings.Contains(string(out), "cdroot")
if i.cfg.EjectCD && bootedFromCD {
i.cfg.Logger.Infof("Writing eject script")
err = i.cfg.Fs.WriteFile("/usr/lib/systemd/system-shutdown/eject", []byte(cnst.EjectScript), 0744)
if err != nil {

View File

@ -20,13 +20,14 @@ import (
"bytes"
"errors"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"path/filepath"
"regexp"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/action"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
@ -42,7 +43,7 @@ const partTmpl = `
%d:%ss:%ss:2048s:ext4::type=83;`
var _ = Describe("Install action tests", func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var fs vfs.FS
var logger v1.Logger
@ -69,15 +70,15 @@ var _ = Describe("Install action tests", func() {
Expect(err).Should(BeNil())
cloudInit = &v1mock.FakeCloudInitRunner{}
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
conf.WithCloudInitRunner(cloudInit),
conf.WithImageExtractor(extractor),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
agentConfig.WithCloudInitRunner(cloudInit),
agentConfig.WithImageExtractor(extractor),
)
})
@ -94,7 +95,7 @@ var _ = Describe("Install action tests", func() {
BeforeEach(func() {
device = "/some/device"
err = utils.MkdirAll(fs, filepath.Dir(device), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(device), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(device)
Expect(err).ShouldNot(HaveOccurred())
@ -143,14 +144,14 @@ var _ = Describe("Install action tests", func() {
}
}
// Need to create the IsoBaseTree, like if we are booting from iso
err = utils.MkdirAll(fs, constants.IsoBaseTree, constants.DirPerm)
err = fsutils.MkdirAll(fs, constants.IsoBaseTree, constants.DirPerm)
Expect(err).To(BeNil())
spec = conf.NewInstallSpec(config)
spec = agentConfig.NewInstallSpec(config)
spec.Active.Size = 16
grubCfg := filepath.Join(spec.Active.MountPoint, constants.GrubConf)
err = utils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(grubCfg)
Expect(err).To(BeNil())
@ -215,7 +216,7 @@ var _ = Describe("Install action tests", func() {
})
It("Sets the executable /run/cos/ejectcd so systemd can eject the cd on restart", func() {
_ = utils.MkdirAll(fs, "/usr/lib/systemd/system-shutdown", constants.DirPerm)
_ = fsutils.MkdirAll(fs, "/usr/lib/systemd/system-shutdown", constants.DirPerm)
_, err := fs.Stat("/usr/lib/systemd/system-shutdown/eject")
Expect(err).To(HaveOccurred())
// Override cmdline to return like we are booting from cd
@ -233,7 +234,7 @@ var _ = Describe("Install action tests", func() {
})
It("ejectcd does nothing if we are not booting from cd", func() {
_ = utils.MkdirAll(fs, "/usr/lib/systemd/system-shutdown", constants.DirPerm)
_ = fsutils.MkdirAll(fs, "/usr/lib/systemd/system-shutdown", constants.DirPerm)
_, err := fs.Stat("/usr/lib/systemd/system-shutdown/eject")
Expect(err).To(HaveOccurred())
spec.Target = device
@ -265,7 +266,7 @@ var _ = Describe("Install action tests", func() {
It("Successfully installs and adds remote cloud-config", Label("cloud-config"), func() {
spec.Target = device
spec.CloudInit = []string{"http://my.config.org"}
utils.MkdirAll(fs, constants.OEMDir, constants.DirPerm)
fsutils.MkdirAll(fs, constants.OEMDir, constants.DirPerm)
_, err := fs.Create(filepath.Join(constants.OEMDir, "90_custom.yaml"))
Expect(err).ShouldNot(HaveOccurred())
Expect(installer.Run()).To(BeNil())

View File

@ -18,6 +18,7 @@ package action
import (
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"path/filepath"
"time"
@ -44,11 +45,11 @@ func (r *ResetAction) resetHook(hook string, chroot bool) error {
}
type ResetAction struct {
cfg *v1.Config
cfg *agentConfig.Config
spec *v1.ResetSpec
}
func NewResetAction(cfg *v1.Config, spec *v1.ResetSpec) *ResetAction {
func NewResetAction(cfg *agentConfig.Config, spec *v1.ResetSpec) *ResetAction {
return &ResetAction{cfg: cfg, spec: spec}
}

View File

@ -20,13 +20,14 @@ import (
"bytes"
"errors"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"path/filepath"
"regexp"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/action"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
@ -37,7 +38,7 @@ import (
)
var _ = Describe("Reset action tests", func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var fs vfs.FS
var logger v1.Logger
@ -63,15 +64,15 @@ var _ = Describe("Reset action tests", func() {
Expect(err).Should(BeNil())
cloudInit = &v1mock.FakeCloudInitRunner{}
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
conf.WithCloudInitRunner(cloudInit),
conf.WithImageExtractor(extractor),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
agentConfig.WithCloudInitRunner(cloudInit),
agentConfig.WithImageExtractor(extractor),
)
})
@ -87,7 +88,7 @@ var _ = Describe("Reset action tests", func() {
Expect(err).ShouldNot(HaveOccurred())
cmdFail = ""
recoveryImg := filepath.Join(constants.RunningStateDir, "cOS", constants.RecoveryImgFile)
err = utils.MkdirAll(fs, filepath.Dir(recoveryImg), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(recoveryImg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(recoveryImg)
Expect(err).To(BeNil())
@ -140,14 +141,14 @@ var _ = Describe("Reset action tests", func() {
}
}
spec, err = conf.NewResetSpec(config)
spec, err = agentConfig.NewResetSpec(config)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Active.Source.IsEmpty()).To(BeFalse())
spec.Active.Size = 16
grubCfg := filepath.Join(spec.Active.MountPoint, spec.GrubConf)
err = utils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(grubCfg)
Expect(err).To(BeNil())
@ -175,7 +176,7 @@ var _ = Describe("Reset action tests", func() {
Expect(reset.Run()).To(BeNil())
})
It("Successfully resets from a squashfs recovery image", Label("channel"), func() {
err := utils.MkdirAll(config.Fs, constants.IsoBaseTree, constants.DirPerm)
err := fsutils.MkdirAll(config.Fs, constants.IsoBaseTree, constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
spec.Active.Source = v1.NewDirSrc(constants.IsoBaseTree)
Expect(reset.Run()).To(BeNil())

View File

@ -18,6 +18,8 @@ package action
import (
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"path/filepath"
"time"
@ -29,11 +31,11 @@ import (
// UpgradeAction represents the struct that will run the upgrade from start to finish
type UpgradeAction struct {
config *v1.Config
config *agentConfig.Config
spec *v1.UpgradeSpec
}
func NewUpgradeAction(config *v1.Config, spec *v1.UpgradeSpec) *UpgradeAction {
func NewUpgradeAction(config *agentConfig.Config, spec *v1.UpgradeSpec) *UpgradeAction {
return &UpgradeAction{config: config, spec: spec}
}
@ -154,7 +156,7 @@ func (u *UpgradeAction) Run() (err error) {
persistentPart := u.spec.Partitions.Persistent
if persistentPart != nil {
// Create the dir otherwise the check for mounted dir fails
_ = utils.MkdirAll(u.config.Fs, persistentPart.MountPoint, constants.DirPerm)
_ = fsutils.MkdirAll(u.config.Fs, persistentPart.MountPoint, constants.DirPerm)
if mnt, err := utils.IsMounted(u.config, persistentPart); !mnt && err == nil {
u.Debug("mounting persistent partition")
umount, err = e.MountRWPartition(persistentPart)
@ -281,7 +283,7 @@ func (u *UpgradeAction) Run() (err error) {
// remove attempts to remove the given path. Does nothing if it doesn't exist
func (u *UpgradeAction) remove(path string) error {
if exists, _ := utils.Exists(u.config.Fs, path); exists {
if exists, _ := fsutils.Exists(u.config.Fs, path); exists {
u.Debug("[Cleanup] Removing %s", path)
return u.config.Fs.RemoveAll(path)
}

View File

@ -19,14 +19,14 @@ package action_test
import (
"bytes"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"path/filepath"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/action"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -36,7 +36,7 @@ import (
)
var _ = Describe("Runtime Actions", func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var fs vfs.FS
var logger v1.Logger
@ -63,16 +63,16 @@ var _ = Describe("Runtime Actions", func() {
Expect(err).Should(BeNil())
cloudInit = &v1mock.FakeCloudInitRunner{}
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
conf.WithCloudInitRunner(cloudInit),
conf.WithImageExtractor(extractor),
conf.WithPlatform("linux/amd64"),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
agentConfig.WithCloudInitRunner(cloudInit),
agentConfig.WithImageExtractor(extractor),
agentConfig.WithPlatform("linux/amd64"),
)
})
@ -98,8 +98,8 @@ var _ = Describe("Runtime Actions", func() {
logger.SetLevel(logrus.DebugLevel)
// Create paths used by tests
utils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.RunningStateDir), constants.DirPerm)
utils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.LiveDir), constants.DirPerm)
fsutils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.RunningStateDir), constants.DirPerm)
fsutils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.LiveDir), constants.DirPerm)
mainDisk := block.Disk{
Name: "device",
@ -143,10 +143,10 @@ var _ = Describe("Runtime Actions", func() {
Describe(fmt.Sprintf("Booting from %s", constants.ActiveLabel), Label("active_label"), func() {
var err error
BeforeEach(func() {
spec, err = conf.NewUpgradeSpec(config)
spec, err = agentConfig.NewUpgradeSpec(config)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(config.Fs, filepath.Join(spec.Active.MountPoint, "etc"), constants.DirPerm)
err = fsutils.MkdirAll(config.Fs, filepath.Join(spec.Active.MountPoint, "etc"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(
@ -303,7 +303,7 @@ var _ = Describe("Runtime Actions", func() {
Expect(err).To(HaveOccurred())
})
It("Successfully upgrades from directory", Label("directory"), func() {
dirSrc, _ := utils.TempDir(fs, "", "elementalupgrade")
dirSrc, _ := fsutils.TempDir(fs, "", "elementalupgrade")
// Create the dir on real os as rsync works on the real os
defer fs.RemoveAll(dirSrc)
spec.Active.Source = v1.NewDirSrc(dirSrc)
@ -346,10 +346,10 @@ var _ = Describe("Runtime Actions", func() {
Describe(fmt.Sprintf("Booting from %s", constants.PassiveLabel), Label("passive_label"), func() {
var err error
BeforeEach(func() {
spec, err = conf.NewUpgradeSpec(config)
spec, err = agentConfig.NewUpgradeSpec(config)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(config.Fs, filepath.Join(spec.Active.MountPoint, "etc"), constants.DirPerm)
err = fsutils.MkdirAll(config.Fs, filepath.Join(spec.Active.MountPoint, "etc"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(
@ -428,7 +428,7 @@ var _ = Describe("Runtime Actions", func() {
err = fs.WriteFile(recoveryImgSquash, []byte("recovery"), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
spec, err = conf.NewUpgradeSpec(config)
spec, err = agentConfig.NewUpgradeSpec(config)
Expect(err).ShouldNot(HaveOccurred())
spec.Active.Size = 10
spec.Passive.Size = 10
@ -484,7 +484,7 @@ var _ = Describe("Runtime Actions", func() {
})
It("Successfully upgrades recovery from directory", Label("directory"), func() {
srcDir, _ := utils.TempDir(fs, "", "elemental")
srcDir, _ := fsutils.TempDir(fs, "", "elemental")
// create a random file on it
_ = fs.WriteFile(fmt.Sprintf("%s/file.file", srcDir), []byte("something"), constants.FilePerm)
@ -513,7 +513,7 @@ var _ = Describe("Runtime Actions", func() {
err = fs.WriteFile(recoveryImg, []byte("recovery"), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
spec, err = conf.NewUpgradeSpec(config)
spec, err = agentConfig.NewUpgradeSpec(config)
Expect(err).ShouldNot(HaveOccurred())
spec.Active.Size = 10
@ -569,7 +569,7 @@ var _ = Describe("Runtime Actions", func() {
}
})
It("Successfully upgrades recovery from directory", Label("directory"), func() {
srcDir, _ := utils.TempDir(fs, "", "elemental")
srcDir, _ := fsutils.TempDir(fs, "", "elemental")
// create a random file on it
_ = fs.WriteFile(fmt.Sprintf("%s/file.file", srcDir), []byte("something"), constants.FilePerm)

View File

@ -20,6 +20,7 @@ import (
"bytes"
"errors"
"fmt"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"io/ioutil"
"log"
"os"
@ -29,7 +30,6 @@ import (
. "github.com/kairos-io/kairos-agent/v2/pkg/cloudinit"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
"github.com/twpayne/go-vfs/vfst"
@ -110,9 +110,9 @@ stages:
logger = v1.NewBufferLogger(logs)
afs, cleanup, _ = vfst.NewTestFS(nil)
err := utils.MkdirAll(afs, "/some/yip", constants.DirPerm)
err := fsutils.MkdirAll(afs, "/some/yip", constants.DirPerm)
Expect(err).To(BeNil())
_ = utils.MkdirAll(afs, "/dev", constants.DirPerm)
_ = fsutils.MkdirAll(afs, "/dev", constants.DirPerm)
device = "/dev/device"
_, err = afs.Create(device)
Expect(err).To(BeNil())

View File

@ -2,17 +2,24 @@ package config
import (
"fmt"
"github.com/spf13/viper"
"os"
"path/filepath"
"runtime"
"strings"
"unicode"
"github.com/kairos-io/kairos-agent/v2/pkg/cloudinit"
"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"
"github.com/kairos-io/kairos-sdk/bundles"
"github.com/kairos-io/kairos-sdk/collector"
"github.com/kairos-io/kairos-sdk/schema"
yip "github.com/mudler/yip/pkg/schema"
"github.com/twpayne/go-vfs"
"gopkg.in/yaml.v3"
"k8s.io/mount-utils"
)
const (
@ -36,6 +43,60 @@ type Install struct {
BindMounts []string `yaml:"bind_mounts,omitempty"`
}
func NewConfig(opts ...GenericOptions) *Config {
log := v1.NewLogger()
hostPlatform, err := v1.NewPlatformFromArch(runtime.GOARCH)
if err != nil {
log.Errorf("error parsing default platform (%s): %s", runtime.GOARCH, err.Error())
return nil
}
arch, err := golangArchToArch(runtime.GOARCH)
if err != nil {
log.Errorf("invalid arch: %s", err.Error())
return nil
}
c := &Config{
Fs: vfs.OSFS,
Logger: log,
Syscall: &v1.RealSyscall{},
Client: http.NewClient(),
Arch: arch,
Platform: hostPlatform,
SquashFsCompressionConfig: constants.GetDefaultSquashfsCompressionOptions(),
ImageExtractor: v1.OCIImageExtractor{},
}
for _, o := range opts {
o(c)
}
// delay runner creation after we have run over the options in case we use WithRunner
if c.Runner == nil {
c.Runner = &v1.RealRunner{Logger: c.Logger}
}
// Now check if the runner has a logger inside, otherwise point our logger into it
// This can happen if we set the WithRunner option as that doesn't set a logger
if c.Runner.GetLogger() == nil {
c.Runner.SetLogger(c.Logger)
}
// Delay the yip runner creation, so we set the proper logger instead of blindly setting it to the logger we create
// at the start of NewConfig, as WithLogger can be passed on init, and that would result in 2 different logger
// instances, on the config.Logger and the other on config.CloudInitRunner
if c.CloudInitRunner == nil {
c.CloudInitRunner = cloudinit.NewYipCloudInitRunner(c.Logger, c.Runner, vfs.OSFS)
}
if c.Mounter == nil {
c.Mounter = mount.New(constants.MountBinary)
}
return c
}
type Config struct {
Install *Install `yaml:"install,omitempty"`
collector.Config `yaml:"-"`
@ -46,6 +107,148 @@ type Config struct {
Bundles Bundles `yaml:"bundles,omitempty"`
GrubOptions map[string]string `yaml:"grub_options,omitempty"`
Env []string `yaml:"env,omitempty"`
// From elemental
Debug bool `yaml:"debug,omitempty" mapstructure:"debug"`
Strict bool `yaml:"strict,omitempty" mapstructure:"strict"`
CloudInitPaths []string `yaml:"cloud-init-paths,omitempty" mapstructure:"cloud-init-paths"`
EjectCD bool `yaml:"eject-cd,omitempty" mapstructure:"eject-cd"`
Logger v1.Logger
Fs v1.FS
Mounter mount.Interface
Runner v1.Runner
Syscall v1.SyscallInterface
CloudInitRunner v1.CloudInitRunner
ImageExtractor v1.ImageExtractor
Client v1.HTTPClient
Platform *v1.Platform `yaml:"platform,omitempty" mapstructure:"platform"`
Cosign bool `yaml:"cosign,omitempty" mapstructure:"cosign"`
Verify bool `yaml:"verify,omitempty" mapstructure:"verify"`
CosignPubKey string `yaml:"cosign-key,omitempty" mapstructure:"cosign-key"`
Arch string `yaml:"arch,omitempty" mapstructure:"arch"`
SquashFsCompressionConfig []string `yaml:"squash-compression,omitempty" mapstructure:"squash-compression"`
SquashFsNoCompression bool `yaml:"squash-no-compression,omitempty" mapstructure:"squash-no-compression"`
}
// WriteInstallState writes the state.yaml file to the given state and recovery paths
func (c Config) WriteInstallState(i *v1.InstallState, statePath, recoveryPath string) error {
data, err := yaml.Marshal(i)
if err != nil {
return err
}
data = append([]byte("# Autogenerated file by elemental client, do not edit\n\n"), data...)
err = c.Fs.WriteFile(statePath, data, constants.FilePerm)
if err != nil {
return err
}
err = c.Fs.WriteFile(recoveryPath, data, constants.FilePerm)
if err != nil {
return err
}
return nil
}
// LoadInstallState loads the state.yaml file and unmarshals it to an InstallState object
func (c Config) LoadInstallState() (*v1.InstallState, error) {
installState := &v1.InstallState{}
data, err := c.Fs.ReadFile(filepath.Join(constants.RunningStateDir, constants.InstallStateFile))
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, installState)
if err != nil {
return nil, err
}
return installState, nil
}
// Sanitize checks the consistency of the struct, returns error
// if unsolvable inconsistencies are found
func (c *Config) Sanitize() error {
// If no squashcompression is set, zero the compression parameters
// By default on NewConfig the SquashFsCompressionConfig is set to the default values, and then override
// on config unmarshall.
if c.SquashFsNoCompression {
c.SquashFsCompressionConfig = []string{}
}
if c.Arch != "" {
p, err := v1.NewPlatformFromArch(c.Arch)
if err != nil {
return err
}
c.Platform = p
}
if c.Platform == nil {
p, err := v1.NewPlatformFromArch(runtime.GOARCH)
if err != nil {
return err
}
c.Platform = p
}
return nil
}
type GenericOptions func(a *Config)
func WithFs(fs v1.FS) func(r *Config) {
return func(r *Config) {
r.Fs = fs
}
}
func WithLogger(logger v1.Logger) func(r *Config) {
return func(r *Config) {
r.Logger = logger
}
}
func WithSyscall(syscall v1.SyscallInterface) func(r *Config) {
return func(r *Config) {
r.Syscall = syscall
}
}
func WithMounter(mounter mount.Interface) func(r *Config) {
return func(r *Config) {
r.Mounter = mounter
}
}
func WithRunner(runner v1.Runner) func(r *Config) {
return func(r *Config) {
r.Runner = runner
}
}
func WithClient(client v1.HTTPClient) func(r *Config) {
return func(r *Config) {
r.Client = client
}
}
func WithCloudInitRunner(ci v1.CloudInitRunner) func(r *Config) {
return func(r *Config) {
r.CloudInitRunner = ci
}
}
func WithPlatform(platform string) func(r *Config) {
return func(r *Config) {
p, err := v1.ParsePlatform(platform)
if err == nil {
r.Platform = p
}
}
}
func WithImageExtractor(extractor v1.ImageExtractor) func(r *Config) {
return func(r *Config) {
r.ImageExtractor = extractor
}
}
type Bundles []Bundle
@ -113,7 +316,8 @@ func FilterKeys(d []byte) ([]byte, error) {
}
func Scan(opts ...collector.Option) (c *Config, err error) {
result := &Config{}
// Init new config with some default options
result := NewConfig()
o := &collector.Options{}
if err := o.Apply(opts...); err != nil {
@ -157,6 +361,13 @@ func Scan(opts ...collector.Option) (c *Config, err error) {
}
}
// If we got debug enabled via cloud config, set it on viper so its available everywhere
if result.Debug {
viper.Set("debug", true)
}
// Config the logger
configLogger(result.Logger, result.Fs)
return result, nil
}
@ -207,3 +418,16 @@ func MergeYAML(objs ...interface{}) ([]byte, error) {
func AddHeader(header, data string) string {
return fmt.Sprintf("%s\n%s", header, data)
}
var errInvalidArch = fmt.Errorf("invalid arch")
func golangArchToArch(arch string) (string, error) {
switch strings.ToLower(arch) {
case constants.ArchAmd64:
return constants.Archx86, nil
case constants.ArchArm64:
return constants.ArchArm64, nil
default:
return "", errInvalidArch
}
}

View File

@ -17,36 +17,22 @@ package config_test
import (
"fmt"
"os"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
v1mocks "github.com/kairos-io/kairos-agent/v2/tests/mocks"
"github.com/twpayne/go-vfs"
"github.com/twpayne/go-vfs/vfst"
"path/filepath"
"reflect"
"strings"
. "github.com/kairos-io/kairos-sdk/schema"
. "github.com/kairos-io/kairos-agent/v2/pkg/config"
. "github.com/kairos-io/kairos-sdk/schema"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
type TConfig struct {
Kairos struct {
OtherKey string `yaml:"other_key"`
NetworkToken string `yaml:"network_token"`
} `yaml:"kairos"`
}
var _ = Describe("Config", func() {
var d string
BeforeEach(func() {
d, _ = os.MkdirTemp("", "xxxx")
})
AfterEach(func() {
if d != "" {
os.RemoveAll(d)
}
})
})
func getTagName(s string) string {
if len(s) < 1 {
return ""
@ -59,7 +45,12 @@ func getTagName(s string) string {
f := func(c rune) bool {
return c == '"' || c == ','
}
return s[:strings.IndexFunc(s, f)]
index := strings.IndexFunc(s, f)
if index == -1 {
return s
}
return s[:index]
}
func structContainsField(f, t string, str interface{}) bool {
@ -118,4 +109,91 @@ var _ = Describe("Schema", func() {
structFieldsContainedInOtherStruct(Bundle{}, BundleSchema{})
})
})
Describe("Write and load installation state", func() {
var config *Config
var runner *v1mocks.FakeRunner
var fs vfs.FS
var mounter *v1mocks.ErrorMounter
var cleanup func()
var err error
var dockerState, channelState *v1.ImageState
var installState *v1.InstallState
var statePath, recoveryPath string
BeforeEach(func() {
runner = v1mocks.NewFakeRunner()
mounter = v1mocks.NewErrorMounter()
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{})
Expect(err).Should(BeNil())
config = NewConfig(
WithFs(fs),
WithRunner(runner),
WithMounter(mounter),
)
dockerState = &v1.ImageState{
Source: v1.NewDockerSrc("registry.org/my/image:tag"),
Label: "active_label",
FS: "ext2",
SourceMetadata: &v1.DockerImageMeta{
Digest: "adadgadg",
Size: 23452345,
},
}
installState = &v1.InstallState{
Date: "somedate",
Partitions: map[string]*v1.PartitionState{
"state": {
FSLabel: "state_label",
Images: map[string]*v1.ImageState{
"active": dockerState,
},
},
"recovery": {
FSLabel: "state_label",
Images: map[string]*v1.ImageState{
"recovery": channelState,
},
},
},
}
statePath = filepath.Join(constants.RunningStateDir, constants.InstallStateFile)
recoveryPath = "/recoverypart/state.yaml"
err = fsutils.MkdirAll(fs, filepath.Dir(recoveryPath), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fsutils.MkdirAll(fs, filepath.Dir(statePath), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
})
AfterEach(func() {
cleanup()
})
It("Writes and loads an installation data", func() {
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).ShouldNot(HaveOccurred())
loadedInstallState, err := config.LoadInstallState()
Expect(err).ShouldNot(HaveOccurred())
Expect(*loadedInstallState).To(Equal(*installState))
})
It("Fails writing to state partition", func() {
err = fs.RemoveAll(filepath.Dir(statePath))
Expect(err).ShouldNot(HaveOccurred())
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).Should(HaveOccurred())
})
It("Fails writing to recovery partition", func() {
err = fs.RemoveAll(filepath.Dir(statePath))
Expect(err).ShouldNot(HaveOccurred())
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).Should(HaveOccurred())
})
It("Fails loading state file", func() {
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).ShouldNot(HaveOccurred())
err = fs.RemoveAll(filepath.Dir(statePath))
_, err = config.LoadInstallState()
Expect(err).Should(HaveOccurred())
})
})
})

View File

@ -14,156 +14,38 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package elementalConfig
package config
import (
"fmt"
"gopkg.in/yaml.v3"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/partitions"
"path/filepath"
"reflect"
"runtime"
"strings"
"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"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
"github.com/mitchellh/mapstructure"
"github.com/sanity-io/litter"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/twpayne/go-vfs"
"k8s.io/mount-utils"
)
type GenericOptions func(a *v1.Config)
func WithFs(fs v1.FS) func(r *v1.Config) {
return func(r *v1.Config) {
r.Fs = fs
}
}
func WithLogger(logger v1.Logger) func(r *v1.Config) {
return func(r *v1.Config) {
r.Logger = logger
}
}
func WithSyscall(syscall v1.SyscallInterface) func(r *v1.Config) {
return func(r *v1.Config) {
r.Syscall = syscall
}
}
func WithMounter(mounter mount.Interface) func(r *v1.Config) {
return func(r *v1.Config) {
r.Mounter = mounter
}
}
func WithRunner(runner v1.Runner) func(r *v1.Config) {
return func(r *v1.Config) {
r.Runner = runner
}
}
func WithClient(client v1.HTTPClient) func(r *v1.Config) {
return func(r *v1.Config) {
r.Client = client
}
}
func WithCloudInitRunner(ci v1.CloudInitRunner) func(r *v1.Config) {
return func(r *v1.Config) {
r.CloudInitRunner = ci
}
}
func WithPlatform(platform string) func(r *v1.Config) {
return func(r *v1.Config) {
p, err := v1.ParsePlatform(platform)
if err == nil {
r.Platform = p
}
}
}
func WithImageExtractor(extractor v1.ImageExtractor) func(r *v1.Config) {
return func(r *v1.Config) {
r.ImageExtractor = extractor
}
}
func NewConfig(opts ...GenericOptions) *v1.Config {
log := v1.NewLogger()
defaultPlatform, err := v1.NewPlatformFromArch(runtime.GOARCH)
if err != nil {
log.Errorf("error parsing default platform (%s): %s", runtime.GOARCH, err.Error())
return nil
}
arch, err := utils.GolangArchToArch(runtime.GOARCH)
if err != nil {
log.Errorf("invalid arch: %s", err.Error())
return nil
}
c := &v1.Config{
Fs: vfs.OSFS,
Logger: log,
Syscall: &v1.RealSyscall{},
Client: http.NewClient(),
Arch: arch,
Platform: defaultPlatform,
SquashFsCompressionConfig: constants.GetDefaultSquashfsCompressionOptions(),
}
for _, o := range opts {
o(c)
}
// delay runner creation after we have run over the options in case we use WithRunner
if c.Runner == nil {
c.Runner = &v1.RealRunner{Logger: c.Logger}
}
// Now check if the runner has a logger inside, otherwise point our logger into it
// This can happen if we set the WithRunner option as that doesn't set a logger
if c.Runner.GetLogger() == nil {
c.Runner.SetLogger(c.Logger)
}
// Delay the yip runner creation, so we set the proper logger instead of blindly setting it to the logger we create
// at the start of NewConfig, as WithLogger can be passed on init, and that would result in 2 different logger
// instances, on the config.Logger and the other on config.CloudInitRunner
if c.CloudInitRunner == nil {
c.CloudInitRunner = cloudinit.NewYipCloudInitRunner(c.Logger, c.Runner, vfs.OSFS)
}
if c.Mounter == nil {
c.Mounter = mount.New(constants.MountBinary)
}
return c
}
// NewInstallSpec returns an InstallSpec struct all based on defaults and basic host checks (e.g. EFI vs BIOS)
func NewInstallSpec(cfg *v1.Config) *v1.InstallSpec {
func NewInstallSpec(cfg *Config) *v1.InstallSpec {
var firmware string
var recoveryImg, activeImg, passiveImg v1.Image
recoveryImgFile := filepath.Join(constants.LiveDir, constants.RecoverySquashFile)
// Check if current host has EFI firmware
efiExists, _ := utils.Exists(cfg.Fs, constants.EfiDevice)
efiExists, _ := fsutils.Exists(cfg.Fs, constants.EfiDevice)
// Check the default ISO installation media is available
isoRootExists, _ := utils.Exists(cfg.Fs, constants.IsoBaseTree)
isoRootExists, _ := fsutils.Exists(cfg.Fs, constants.IsoBaseTree)
// Check the default ISO recovery installation media is available)
recoveryExists, _ := utils.Exists(cfg.Fs, recoveryImgFile)
recoveryExists, _ := fsutils.Exists(cfg.Fs, recoveryImgFile)
if efiExists {
firmware = v1.EFI
@ -206,7 +88,7 @@ func NewInstallSpec(cfg *v1.Config) *v1.InstallSpec {
return &v1.InstallSpec{
Firmware: firmware,
PartTable: v1.GPT,
Partitions: NewInstallElementalParitions(),
Partitions: NewInstallElementalPartitions(),
GrubConf: constants.GrubConf,
Tty: constants.DefaultTty,
Active: activeImg,
@ -215,9 +97,9 @@ func NewInstallSpec(cfg *v1.Config) *v1.InstallSpec {
}
}
func NewInstallElementalParitions() v1.ElementalPartitions {
partitions := v1.ElementalPartitions{}
partitions.OEM = &v1.Partition{
func NewInstallElementalPartitions() v1.ElementalPartitions {
pt := v1.ElementalPartitions{}
pt.OEM = &v1.Partition{
FilesystemLabel: constants.OEMLabel,
Size: constants.OEMSize,
Name: constants.OEMPartName,
@ -226,7 +108,7 @@ func NewInstallElementalParitions() v1.ElementalPartitions {
Flags: []string{},
}
partitions.Recovery = &v1.Partition{
pt.Recovery = &v1.Partition{
FilesystemLabel: constants.RecoveryLabel,
Size: constants.RecoverySize,
Name: constants.RecoveryPartName,
@ -235,7 +117,7 @@ func NewInstallElementalParitions() v1.ElementalPartitions {
Flags: []string{},
}
partitions.State = &v1.Partition{
pt.State = &v1.Partition{
FilesystemLabel: constants.StateLabel,
Size: constants.StateSize,
Name: constants.StatePartName,
@ -244,7 +126,7 @@ func NewInstallElementalParitions() v1.ElementalPartitions {
Flags: []string{},
}
partitions.Persistent = &v1.Partition{
pt.Persistent = &v1.Partition{
FilesystemLabel: constants.PersistentLabel,
Size: constants.PersistentSize,
Name: constants.PersistentPartName,
@ -252,11 +134,11 @@ func NewInstallElementalParitions() v1.ElementalPartitions {
MountPoint: constants.PersistentDir,
Flags: []string{},
}
return partitions
return pt
}
// NewUpgradeSpec returns an UpgradeSpec struct all based on defaults and current host state
func NewUpgradeSpec(cfg *v1.Config) (*v1.UpgradeSpec, error) {
func NewUpgradeSpec(cfg *Config) (*v1.UpgradeSpec, error) {
var recLabel, recFs, recMnt string
var active, passive, recovery v1.Image
@ -265,7 +147,7 @@ func NewUpgradeSpec(cfg *v1.Config) (*v1.UpgradeSpec, error) {
cfg.Logger.Warnf("failed reading installation state: %s", err.Error())
}
parts, err := utils.GetAllPartitions()
parts, err := partitions.GetAllPartitions()
if err != nil {
return nil, fmt.Errorf("could not read host partitions")
}
@ -273,17 +155,17 @@ func NewUpgradeSpec(cfg *v1.Config) (*v1.UpgradeSpec, error) {
if ep.Recovery == nil {
// We could have recovery in lvm which won't appear in ghw list
ep.Recovery = utils.GetPartitionViaDM(cfg.Fs, constants.RecoveryLabel)
ep.Recovery = partitions.GetPartitionViaDM(cfg.Fs, constants.RecoveryLabel)
}
if ep.OEM == nil {
// We could have OEM in lvm which won't appear in ghw list
ep.OEM = utils.GetPartitionViaDM(cfg.Fs, constants.OEMLabel)
ep.OEM = partitions.GetPartitionViaDM(cfg.Fs, constants.OEMLabel)
}
if ep.Persistent == nil {
// We could have persistent encrypted or in lvm which won't appear in ghw list
ep.Persistent = utils.GetPartitionViaDM(cfg.Fs, constants.PersistentLabel)
ep.Persistent = partitions.GetPartitionViaDM(cfg.Fs, constants.PersistentLabel)
}
if ep.Recovery != nil {
@ -291,7 +173,7 @@ func NewUpgradeSpec(cfg *v1.Config) (*v1.UpgradeSpec, error) {
ep.Recovery.MountPoint = constants.RecoveryDir
}
squashedRec, err := utils.HasSquashedRecovery(cfg, ep.Recovery)
squashedRec, err := hasSquashedRecovery(cfg, ep.Recovery)
if err != nil {
return nil, fmt.Errorf("failed checking for squashed recovery")
}
@ -361,23 +243,23 @@ func NewUpgradeSpec(cfg *v1.Config) (*v1.UpgradeSpec, error) {
}
// NewResetSpec returns a ResetSpec struct all based on defaults and current host state
func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
func NewResetSpec(cfg *Config) (*v1.ResetSpec, error) {
var imgSource *v1.ImageSource
//TODO find a way to pre-load current state values such as labels
if !utils.BootedFrom(cfg.Runner, constants.RecoverySquashFile) &&
!utils.BootedFrom(cfg.Runner, constants.SystemLabel) {
if !BootedFrom(cfg.Runner, constants.RecoverySquashFile) &&
!BootedFrom(cfg.Runner, constants.SystemLabel) {
return nil, fmt.Errorf("reset can only be called from the recovery system")
}
efiExists, _ := utils.Exists(cfg.Fs, constants.EfiDevice)
efiExists, _ := fsutils.Exists(cfg.Fs, constants.EfiDevice)
installState, err := cfg.LoadInstallState()
if err != nil {
cfg.Logger.Warnf("failed reading installation state: %s", err.Error())
}
parts, err := utils.GetAllPartitions()
parts, err := partitions.GetAllPartitions()
if err != nil {
return nil, fmt.Errorf("could not read host partitions")
}
@ -403,7 +285,7 @@ func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
if ep.Recovery == nil {
// We could have recovery in lvm which won't appear in ghw list
ep.Recovery = utils.GetPartitionViaDM(cfg.Fs, constants.RecoveryLabel)
ep.Recovery = partitions.GetPartitionViaDM(cfg.Fs, constants.RecoveryLabel)
if ep.Recovery == nil {
return nil, fmt.Errorf("recovery partition not found")
}
@ -422,7 +304,7 @@ func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
ep.OEM.Name = constants.OEMPartName
} else {
// We could have oem in lvm which won't appear in ghw list
ep.OEM = utils.GetPartitionViaDM(cfg.Fs, constants.OEMLabel)
ep.OEM = partitions.GetPartitionViaDM(cfg.Fs, constants.OEMLabel)
}
if ep.OEM == nil {
@ -437,7 +319,7 @@ func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
ep.Persistent.Name = constants.PersistentPartName
} else {
// We could have persistent encrypted or in lvm which won't appear in ghw list
ep.Persistent = utils.GetPartitionViaDM(cfg.Fs, constants.PersistentLabel)
ep.Persistent = partitions.GetPartitionViaDM(cfg.Fs, constants.PersistentLabel)
}
if ep.Persistent == nil {
cfg.Logger.Warnf("no Persistent partition found")
@ -446,11 +328,11 @@ func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
recoveryImg := filepath.Join(constants.RunningStateDir, "cOS", constants.RecoveryImgFile)
recoveryImg2 := filepath.Join(constants.RunningRecoveryStateDir, "cOS", constants.RecoveryImgFile)
if exists, _ := utils.Exists(cfg.Fs, recoveryImg); exists {
if exists, _ := fsutils.Exists(cfg.Fs, recoveryImg); exists {
imgSource = v1.NewFileSrc(recoveryImg)
} else if exists, _ = utils.Exists(cfg.Fs, recoveryImg2); exists {
} else if exists, _ = fsutils.Exists(cfg.Fs, recoveryImg2); exists {
imgSource = v1.NewFileSrc(recoveryImg2)
} else if exists, _ = utils.Exists(cfg.Fs, constants.IsoBaseTree); exists {
} else if exists, _ = fsutils.Exists(cfg.Fs, constants.IsoBaseTree); exists {
imgSource = v1.NewDirSrc(constants.IsoBaseTree)
} else {
imgSource = v1.NewEmptySrc()
@ -484,35 +366,38 @@ func NewResetSpec(cfg *v1.Config) (*v1.ResetSpec, error) {
}, nil
}
// ReadConfigRunFromAgentConfig reads the configuration directly from a given cloud config string
func ReadConfigRunFromAgentConfig(c *agentConfig.Config) (*v1.Config, error) {
cfg := NewConfig(WithLogger(v1.NewLogger()), WithImageExtractor(v1.OCIImageExtractor{}))
var err error
ccString, err := c.Config.String()
// ReadResetSpecFromConfig will return a proper v1.ResetSpec based on an agent Config
func ReadResetSpecFromConfig(c *Config) (*v1.ResetSpec, error) {
sp, err := ReadSpecFromCloudConfig(c, "reset")
if err != nil {
return nil, err
return &v1.ResetSpec{}, err
}
resetSpec := sp.(*v1.ResetSpec)
return resetSpec, nil
}
// Load any cloud-config values that override our default Config
err = yaml.Unmarshal([]byte(ccString), &cfg)
// ReadInstallSpecFromConfig will return a proper v1.InstallSpec based on an agent Config
func ReadInstallSpecFromConfig(c *Config) (*v1.InstallSpec, error) {
sp, err := ReadSpecFromCloudConfig(c, "install")
if err != nil {
return nil, err
return &v1.InstallSpec{}, err
}
// If we got debug enabled via cloud config, set it on viper so its available everywhere
if cfg.Debug {
viper.Set("debug", true)
installSpec := sp.(*v1.InstallSpec)
return installSpec, nil
}
configLogger(cfg.Logger, cfg.Fs)
// Store the full cloud-config in here, so we can reuse it afterward for the spec
cfg.FullCloudConfig = ccString
err = cfg.Sanitize()
cfg.Logger.Debugf("Full config loaded: %s", litter.Sdump(cfg))
return cfg, err
// ReadUpgradeSpecFromConfig will return a proper v1.UpgradeSpec based on an agent Config
func ReadUpgradeSpecFromConfig(c *Config) (*v1.UpgradeSpec, error) {
sp, err := ReadSpecFromCloudConfig(c, "reset")
if err != nil {
return &v1.UpgradeSpec{}, err
}
upgradeSpec := sp.(*v1.UpgradeSpec)
return upgradeSpec, nil
}
// ReadSpecFromCloudConfig returns a v1.Spec for the given spec
func ReadSpecFromCloudConfig(r *v1.Config, spec string) (v1.Spec, error) {
func ReadSpecFromCloudConfig(r *Config, spec string) (v1.Spec, error) {
var sp v1.Spec
var err error
@ -531,8 +416,12 @@ func ReadSpecFromCloudConfig(r *v1.Config, spec string) (v1.Spec, error) {
}
// Load the config into viper from the raw cloud config string
ccString, err := r.String()
if err != nil {
return nil, fmt.Errorf("failed initializing spec: %v", err)
}
viper.SetConfigType("yaml")
viper.ReadConfig(strings.NewReader(r.FullCloudConfig))
viper.ReadConfig(strings.NewReader(ccString))
vp := viper.Sub(spec)
if vp == nil {
vp = viper.New()
@ -546,47 +435,6 @@ func ReadSpecFromCloudConfig(r *v1.Config, spec string) (v1.Spec, error) {
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.Config, v1.Spec, error) {
config, err := ReadConfigRunFromAgentConfig(c)
if err != nil {
return nil, nil, err
}
spec, err := ReadSpecFromCloudConfig(config, action)
if err != nil {
return nil, nil, err
}
return config, spec, nil
}
// ReadResetConfigFromAgentConfig will return a proper v1.Config and v1.ResetSpec based on an agent Config
func ReadResetConfigFromAgentConfig(c *agentConfig.Config) (*v1.Config, *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.Config, *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.Config, *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) {
// Set debug level
if viper.GetBool("debug") {
@ -676,3 +524,52 @@ func setDecoder(config *mapstructure.DecoderConfig) {
// we got form configs.
config.ZeroFields = true
}
// BootedFrom will check if we are booting from the given label
func BootedFrom(runner v1.Runner, label string) bool {
out, _ := runner.Run("cat", "/proc/cmdline")
return strings.Contains(string(out), label)
}
// HasSquashedRecovery returns true if a squashed recovery image is found in the system
func hasSquashedRecovery(config *Config, recovery *v1.Partition) (squashed bool, err error) {
mountPoint := recovery.MountPoint
if mnt, _ := isMounted(config, recovery); !mnt {
tmpMountDir, err := fsutils.TempDir(config.Fs, "", "elemental")
if err != nil {
config.Logger.Errorf("failed creating temporary dir: %v", err)
return false, err
}
defer config.Fs.RemoveAll(tmpMountDir) // nolint:errcheck
err = config.Mounter.Mount(recovery.Path, tmpMountDir, "auto", []string{})
if err != nil {
config.Logger.Errorf("failed mounting recovery partition: %v", err)
return false, err
}
mountPoint = tmpMountDir
defer func() {
err = config.Mounter.Unmount(tmpMountDir)
if err != nil {
squashed = false
}
}()
}
return fsutils.Exists(config.Fs, filepath.Join(mountPoint, "cOS", constants.RecoverySquashFile))
}
func isMounted(config *Config, part *v1.Partition) (bool, error) {
if part == nil {
return false, fmt.Errorf("nil partition")
}
if part.MountPoint == "" {
return false, nil
}
// Using IsLikelyNotMountPoint seams to be safe as we are not checking
// for bind mounts here
notMnt, err := config.Mounter.IsLikelyNotMountPoint(part.MountPoint)
if err != nil {
return false, err
}
return !notMnt, nil
}

View File

@ -14,11 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package elementalConfig_test
package config_test
import (
"github.com/kairos-io/kairos-agent/v2/pkg/config"
"fmt"
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/kairos-io/kairos-sdk/collector"
"github.com/sanity-io/litter"
"github.com/sirupsen/logrus"
"k8s.io/mount-utils"
"os"
@ -26,9 +29,7 @@ import (
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
"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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -46,7 +47,7 @@ var _ = Describe("Types", Label("types", "config"), func() {
var sysc *v1mock.FakeSyscall
var logger v1.Logger
var ci *v1mock.FakeCloudInitRunner
var c *v1.Config
var c *config.Config
BeforeEach(func() {
fs, cleanup, err = vfst.NewTestFS(nil)
Expect(err).ToNot(HaveOccurred())
@ -56,15 +57,15 @@ var _ = Describe("Types", Label("types", "config"), func() {
sysc = &v1mock.FakeSyscall{}
logger = v1.NewNullLogger()
ci = &v1mock.FakeCloudInitRunner{}
c = elementalConfig.NewConfig(
elementalConfig.WithFs(fs),
elementalConfig.WithMounter(mounter),
elementalConfig.WithRunner(runner),
elementalConfig.WithSyscall(sysc),
elementalConfig.WithLogger(logger),
elementalConfig.WithCloudInitRunner(ci),
elementalConfig.WithClient(client),
elementalConfig.WithPlatform("linux/arm64"),
c = config.NewConfig(
config.WithFs(fs),
config.WithMounter(mounter),
config.WithRunner(runner),
config.WithSyscall(sysc),
config.WithLogger(logger),
config.WithCloudInitRunner(ci),
config.WithClient(client),
config.WithPlatform("linux/arm64"),
)
})
AfterEach(func() {
@ -87,24 +88,24 @@ var _ = Describe("Types", Label("types", "config"), func() {
fs, cleanup, err := vfst.NewTestFS(nil)
defer cleanup()
Expect(err).ToNot(HaveOccurred())
c := elementalConfig.NewConfig(
elementalConfig.WithFs(fs),
elementalConfig.WithMounter(mounter),
c := config.NewConfig(
config.WithFs(fs),
config.WithMounter(mounter),
)
Expect(c.Fs).To(Equal(fs))
Expect(c.Mounter).To(Equal(mounter))
Expect(c.Runner).ToNot(BeNil())
})
It("defaults to sane platform if the platform is broken", func() {
c = elementalConfig.NewConfig(
elementalConfig.WithFs(fs),
elementalConfig.WithMounter(mounter),
elementalConfig.WithRunner(runner),
elementalConfig.WithSyscall(sysc),
elementalConfig.WithLogger(logger),
elementalConfig.WithCloudInitRunner(ci),
elementalConfig.WithClient(client),
elementalConfig.WithPlatform("wwwwwww"),
c = config.NewConfig(
config.WithFs(fs),
config.WithMounter(mounter),
config.WithRunner(runner),
config.WithSyscall(sysc),
config.WithLogger(logger),
config.WithCloudInitRunner(ci),
config.WithClient(client),
config.WithPlatform("wwwwwww"),
)
Expect(c.Platform.OS).To(Equal("linux"))
Expect(c.Platform.Arch).To(Equal("x86_64"))
@ -116,41 +117,41 @@ var _ = Describe("Types", Label("types", "config"), func() {
runner := v1mock.NewFakeRunner()
sysc := &v1mock.FakeSyscall{}
logger := v1.NewNullLogger()
c := elementalConfig.NewConfig(
elementalConfig.WithRunner(runner),
elementalConfig.WithSyscall(sysc),
elementalConfig.WithLogger(logger),
c := config.NewConfig(
config.WithRunner(runner),
config.WithSyscall(sysc),
config.WithLogger(logger),
)
Expect(c.Mounter).To(Equal(mount.New(constants.MountBinary)))
})
})
Describe("Config", func() {
cfg := elementalConfig.NewConfig(elementalConfig.WithMounter(mounter))
cfg := config.NewConfig(config.WithMounter(mounter))
Expect(cfg.Mounter).To(Equal(mounter))
Expect(cfg.Runner).NotTo(BeNil())
})
Describe("InstallSpec", func() {
It("sets installation defaults from install efi media with recovery", Label("install", "efi"), func() {
// Set EFI firmware detection
err = utils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.EfiDevice)
Expect(err).ShouldNot(HaveOccurred())
// Set ISO base tree detection
err = utils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.IsoBaseTree)
Expect(err).ShouldNot(HaveOccurred())
// Set recovery image detection detection
recoveryImgFile := filepath.Join(constants.LiveDir, constants.RecoverySquashFile)
err = utils.MkdirAll(fs, filepath.Dir(recoveryImgFile), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(recoveryImgFile), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(recoveryImgFile)
Expect(err).ShouldNot(HaveOccurred())
spec := elementalConfig.NewInstallSpec(c)
spec := config.NewInstallSpec(c)
Expect(spec.Firmware).To(Equal(v1.EFI))
Expect(spec.Active.Source.Value()).To(Equal(constants.IsoBaseTree))
Expect(spec.Recovery.Source.Value()).To(Equal(recoveryImgFile))
@ -166,12 +167,12 @@ var _ = Describe("Types", Label("types", "config"), func() {
})
It("sets installation defaults from install bios media without recovery", Label("install", "bios"), func() {
// Set ISO base tree detection
err = utils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.IsoBaseTree)
Expect(err).ShouldNot(HaveOccurred())
spec := elementalConfig.NewInstallSpec(c)
spec := config.NewInstallSpec(c)
Expect(spec.Firmware).To(Equal(v1.BIOS))
Expect(spec.Active.Source.Value()).To(Equal(constants.IsoBaseTree))
Expect(spec.Recovery.Source.Value()).To(Equal(spec.Active.File))
@ -186,7 +187,7 @@ var _ = Describe("Types", Label("types", "config"), func() {
Expect(spec.Partitions.BIOS).NotTo(BeNil())
})
It("sets installation defaults without being on installation media", Label("install"), func() {
spec := elementalConfig.NewInstallSpec(c)
spec := config.NewInstallSpec(c)
Expect(spec.Firmware).To(Equal(v1.BIOS))
Expect(spec.Active.Source.IsEmpty()).To(BeTrue())
Expect(spec.Recovery.Source.Value()).To(Equal(spec.Active.File))
@ -245,18 +246,18 @@ var _ = Describe("Types", Label("types", "config"), func() {
})
It("sets reset defaults on efi from squashed recovery", func() {
// Set EFI firmware detection
err = utils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.EfiDevice)
Expect(err).ShouldNot(HaveOccurred())
// Set squashfs detection
err = utils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.IsoBaseTree), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.IsoBaseTree)
Expect(err).ShouldNot(HaveOccurred())
spec, err := elementalConfig.NewResetSpec(c)
spec, err := config.NewResetSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Active.Source.Value()).To(Equal(constants.IsoBaseTree))
Expect(spec.Partitions.EFI.MountPoint).To(Equal(constants.EfiDir))
@ -264,17 +265,17 @@ var _ = Describe("Types", Label("types", "config"), func() {
It("sets reset defaults on bios from non-squashed recovery", func() {
// Set non-squashfs recovery image detection
recoveryImg := filepath.Join(constants.RunningStateDir, "cOS", constants.RecoveryImgFile)
err = utils.MkdirAll(fs, filepath.Dir(recoveryImg), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(recoveryImg), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(recoveryImg)
Expect(err).ShouldNot(HaveOccurred())
spec, err := elementalConfig.NewResetSpec(c)
spec, err := config.NewResetSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Active.Source.Value()).To(Equal(recoveryImg))
})
It("sets reset defaults on bios from unknown recovery", func() {
spec, err := elementalConfig.NewResetSpec(c)
spec, err := config.NewResetSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Active.Source.IsEmpty()).To(BeTrue())
})
@ -312,13 +313,13 @@ var _ = Describe("Types", Label("types", "config"), func() {
ghwTest.Clean()
})
It("fails to set defaults if not booted from recovery", func() {
_, err := elementalConfig.NewResetSpec(c)
_, err := config.NewResetSpec(c)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("reset can only be called from the recovery system"))
})
It("fails to set defaults if no recovery partition detected", func() {
bootedFrom = constants.SystemLabel
_, err := elementalConfig.NewResetSpec(c)
_, err := config.NewResetSpec(c)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("recovery partition not found"))
})
@ -333,19 +334,19 @@ var _ = Describe("Types", Label("types", "config"), func() {
defer ghwTest.Clean()
bootedFrom = constants.SystemLabel
_, err := elementalConfig.NewResetSpec(c)
_, err := config.NewResetSpec(c)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("state partition not found"))
})
It("fails to set defaults if no efi partition on efi firmware", func() {
// Set EFI firmware detection
err = utils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.EfiDevice), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.EfiDevice)
Expect(err).ShouldNot(HaveOccurred())
bootedFrom = constants.SystemLabel
_, err := elementalConfig.NewResetSpec(c)
_, err := config.NewResetSpec(c)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("EFI partition not found"))
})
@ -394,12 +395,12 @@ var _ = Describe("Types", Label("types", "config"), func() {
ghwTest.Clean()
})
It("sets upgrade defaults for active upgrade", func() {
spec, err := elementalConfig.NewUpgradeSpec(c)
spec, err := config.NewUpgradeSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Active.Source.IsEmpty()).To(BeTrue())
})
It("sets upgrade defaults for non-squashed recovery upgrade", func() {
spec, err := elementalConfig.NewUpgradeSpec(c)
spec, err := config.NewUpgradeSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Recovery.Source.IsEmpty()).To(BeTrue())
Expect(spec.Recovery.FS).To(Equal(constants.LinuxImgFs))
@ -408,12 +409,12 @@ var _ = Describe("Types", Label("types", "config"), func() {
//Set squashed recovery detection
mounter.Mount("device3", constants.LiveDir, "auto", []string{})
img := filepath.Join(constants.LiveDir, "cOS", constants.RecoverySquashFile)
err = utils.MkdirAll(fs, filepath.Dir(img), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(img), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(img)
Expect(err).ShouldNot(HaveOccurred())
spec, err := elementalConfig.NewUpgradeSpec(c)
spec, err := config.NewUpgradeSpec(c)
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Recovery.Source.IsEmpty()).To(BeTrue())
Expect(spec.Recovery.FS).To(Equal(constants.SquashFs))
@ -501,25 +502,25 @@ cloud-init-paths:
It("Reads properly the cloud config for install", func() {
cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs)
Expect(err).ToNot(HaveOccurred())
installConfig, installSpec, err := elementalConfig.ReadInstallConfigFromAgentConfig(cfg)
fmt.Print(litter.Sdump(cfg))
installSpec, err := config.ReadInstallSpecFromConfig(cfg)
Expect(err).ToNot(HaveOccurred())
Expect(installConfig.Strict).To(BeTrue())
Expect(cfg.Strict).To(BeTrue())
Expect(installSpec.GrubDefEntry).To(Equal("MyCustomOS"))
Expect(installSpec.Active.Size).To(Equal(uint(666)))
Expect(installConfig.CloudInitPaths).To(ContainElement("/what"))
Expect(cfg.CloudInitPaths).To(ContainElement("/what"))
})
It("Reads properly the cloud config for reset", func() {
bootedFrom = constants.SystemLabel
cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs)
config, err := elementalConfig.ReadConfigRunFromAgentConfig(cfg)
Expect(err).ToNot(HaveOccurred())
// Override the config with our test params
config.Runner = runner
config.Fs = fs
config.Mounter = mounter
config.CloudInitRunner = ci
spec, err := elementalConfig.ReadSpecFromCloudConfig(config, "reset")
cfg.Runner = runner
cfg.Fs = fs
cfg.Mounter = mounter
cfg.CloudInitRunner = ci
spec, err := config.ReadSpecFromCloudConfig(cfg, "reset")
Expect(err).ToNot(HaveOccurred())
resetSpec := spec.(*v1.ResetSpec)
Expect(resetSpec.FormatPersistent).To(BeTrue())
@ -528,32 +529,28 @@ cloud-init-paths:
})
It("Reads properly the cloud config for upgrade", func() {
cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs)
config, err := elementalConfig.ReadConfigRunFromAgentConfig(cfg)
Expect(err).ToNot(HaveOccurred())
// Override the config with our test params
config.Runner = runner
config.Fs = fs
config.Mounter = mounter
config.CloudInitRunner = ci
spec, err := elementalConfig.ReadSpecFromCloudConfig(config, "upgrade")
cfg.Runner = runner
cfg.Fs = fs
cfg.Mounter = mounter
cfg.CloudInitRunner = ci
spec, err := config.ReadSpecFromCloudConfig(cfg, "upgrade")
Expect(err).ToNot(HaveOccurred())
upgradeSpec := spec.(*v1.UpgradeSpec)
Expect(upgradeSpec.RecoveryUpgrade).To(BeTrue())
})
It("Fails when a wrong action is read", func() {
cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs)
config, err := elementalConfig.ReadConfigRunFromAgentConfig(cfg)
Expect(err).ToNot(HaveOccurred())
_, err = elementalConfig.ReadSpecFromCloudConfig(config, "nope")
_, err = config.ReadSpecFromCloudConfig(cfg, "nope")
Expect(err).To(HaveOccurred())
})
It("Sets info level if its not on the cloud-config", func() {
// Now again but with no config
cfg, err := config.Scan(collector.Directories([]string{""}...), collector.NoLogs)
Expect(err).ToNot(HaveOccurred())
installConfig, _, err := elementalConfig.ReadInstallConfigFromAgentConfig(cfg)
Expect(err).ToNot(HaveOccurred())
Expect(installConfig.Logger.GetLevel()).To(Equal(logrus.InfoLevel))
Expect(cfg.Logger.GetLevel()).To(Equal(logrus.InfoLevel))
})
It("Sets debug level if its on the cloud-config", func() {
ccdata := []byte(`#cloud-config
@ -563,11 +560,19 @@ debug: true
Expect(err).ToNot(HaveOccurred())
cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs)
Expect(err).ToNot(HaveOccurred())
installConfig, _, err := elementalConfig.ReadInstallConfigFromAgentConfig(cfg)
Expect(err).ToNot(HaveOccurred())
Expect(installConfig.Logger.GetLevel()).To(Equal(logrus.DebugLevel))
Expect(cfg.Logger.GetLevel()).To(Equal(logrus.DebugLevel))
})
})
Describe("TestBootedFrom", Label("BootedFrom"), func() {
It("returns true if we are booting from label FAKELABEL", func() {
runner.ReturnValue = []byte("")
Expect(config.BootedFrom(runner, "FAKELABEL")).To(BeFalse())
})
It("returns false if we are not booting from label FAKELABEL", func() {
runner.ReturnValue = []byte("FAKELABEL")
Expect(config.BootedFrom(runner, "FAKELABEL")).To(BeTrue())
})
})
})
})

View File

@ -19,21 +19,23 @@ package elemental
import (
"errors"
"fmt"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"path/filepath"
"strings"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
"github.com/kairos-io/kairos-agent/v2/pkg/partitioner"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
)
// Elemental is the struct meant to self-contain most utils and actions related to Elemental, like installing or applying selinux
type Elemental struct {
config *v1.Config
config *agentConfig.Config
}
func NewElemental(config *v1.Config) *Elemental {
func NewElemental(config *agentConfig.Config) *Elemental {
return &Elemental{
config: config,
}
@ -177,7 +179,7 @@ func (e Elemental) MountRWPartition(part *v1.Partition) (umount func() error, er
// MountPartition mounts a partition with the given mount options
func (e Elemental) MountPartition(part *v1.Partition, opts ...string) error {
e.config.Logger.Debugf("Mounting partition %s", part.FilesystemLabel)
err := utils.MkdirAll(e.config.Fs, part.MountPoint, cnst.DirPerm)
err := fsutils.MkdirAll(e.config.Fs, part.MountPoint, cnst.DirPerm)
if err != nil {
return err
}
@ -211,7 +213,7 @@ func (e Elemental) UnmountPartition(part *v1.Partition) error {
// MountImage mounts an image with the given mount options
func (e Elemental) MountImage(img *v1.Image, opts ...string) error {
e.config.Logger.Debugf("Mounting image %s", img.Label)
err := utils.MkdirAll(e.config.Fs, img.MountPoint, cnst.DirPerm)
err := fsutils.MkdirAll(e.config.Fs, img.MountPoint, cnst.DirPerm)
if err != nil {
return err
}
@ -251,7 +253,7 @@ func (e Elemental) UnmountImage(img *v1.Image) error {
// CreateFileSystemImage creates the image file for config.target
func (e Elemental) CreateFileSystemImage(img *v1.Image) error {
e.config.Logger.Infof("Creating file system image %s", img.File)
err := utils.MkdirAll(e.config.Fs, filepath.Dir(img.File), cnst.DirPerm)
err := fsutils.MkdirAll(e.config.Fs, filepath.Dir(img.File), cnst.DirPerm)
if err != nil {
return err
}
@ -298,7 +300,7 @@ func (e *Elemental) DeployImage(img *v1.Image, leaveMounted bool) (info interfac
}
} else {
target = utils.GetTempDir(e.config, "")
err := utils.MkdirAll(e.config.Fs, target, cnst.DirPerm)
err := fsutils.MkdirAll(e.config.Fs, target, cnst.DirPerm)
if err != nil {
return nil, err
}
@ -374,7 +376,7 @@ func (e *Elemental) DumpSource(target string, imgSrc *v1.ImageSource) (info inte
return nil, err
}
} else if imgSrc.IsFile() {
err := utils.MkdirAll(e.config.Fs, filepath.Dir(target), cnst.DirPerm)
err := fsutils.MkdirAll(e.config.Fs, filepath.Dir(target), cnst.DirPerm)
if err != nil {
return nil, err
}
@ -412,7 +414,7 @@ func (e *Elemental) CopyCloudConfig(cloudInit []string) (err error) {
func (e *Elemental) SelinuxRelabel(rootDir string, raiseError bool) error {
policyFile, err := utils.FindFileWithPrefix(e.config.Fs, filepath.Join(rootDir, cnst.SELinuxTargetedPolicyPath), "policy.")
contextFile := filepath.Join(rootDir, cnst.SELinuxTargetedContextFile)
contextExists, _ := utils.Exists(e.config.Fs, contextFile)
contextExists, _ := fsutils.Exists(e.config.Fs, contextFile)
if err == nil && contextExists && utils.CommandExists("setfiles") {
var out []byte
@ -452,7 +454,7 @@ func (e *Elemental) CheckActiveDeployment(labels []string) bool {
// in cnst.DownloadedIsoMnt
func (e *Elemental) GetIso(iso string) (tmpDir string, err error) {
//TODO support ISO download in persistent storage?
tmpDir, err = utils.TempDir(e.config.Fs, "", "elemental")
tmpDir, err = fsutils.TempDir(e.config.Fs, "", "elemental")
if err != nil {
return "", err
}
@ -470,7 +472,7 @@ func (e *Elemental) GetIso(iso string) (tmpDir string, err error) {
if err != nil {
return "", err
}
err = utils.MkdirAll(e.config.Fs, isoMnt, cnst.DirPerm)
err = fsutils.MkdirAll(e.config.Fs, isoMnt, cnst.DirPerm)
if err != nil {
return "", err
}
@ -486,7 +488,7 @@ func (e *Elemental) GetIso(iso string) (tmpDir string, err error) {
}()
e.config.Logger.Infof("Mounting squashfs image from iso into %s", rootfsMnt)
err = utils.MkdirAll(e.config.Fs, rootfsMnt, cnst.DirPerm)
err = fsutils.MkdirAll(e.config.Fs, rootfsMnt, cnst.DirPerm)
if err != nil {
return "", err
}
@ -505,7 +507,7 @@ func (e Elemental) UpdateSourcesFormDownloadedISO(workDir string, activeImg *v1.
}
if recoveryImg != nil {
squashedImgSource := filepath.Join(isoMnt, cnst.RecoverySquashFile)
if exists, _ := utils.Exists(e.config.Fs, squashedImgSource); exists {
if exists, _ := fsutils.Exists(e.config.Fs, squashedImgSource); exists {
recoveryImg.Source = v1.NewFileSrc(squashedImgSource)
recoveryImg.FS = cnst.SquashFs
} else if activeImg != nil {

View File

@ -19,6 +19,8 @@ package elemental_test
import (
"errors"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"os"
"path/filepath"
"testing"
@ -28,7 +30,6 @@ import (
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
"github.com/kairos-io/kairos-agent/v2/pkg/elemental"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
@ -49,7 +50,7 @@ func TestElementalSuite(t *testing.T) {
}
var _ = Describe("Elemental", Label("elemental"), func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var logger v1.Logger
var syscall v1.SyscallInterface
@ -67,14 +68,14 @@ var _ = Describe("Elemental", Label("elemental"), func() {
logger = v1.NewNullLogger()
fs, cleanup, _ = vfst.NewTestFS(nil)
extractor = v1mock.NewFakeImageExtractor(logger)
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
conf.WithImageExtractor(extractor),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
agentConfig.WithImageExtractor(extractor),
)
})
AfterEach(func() { cleanup() })
@ -82,9 +83,9 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var el *elemental.Elemental
var parts v1.ElementalPartitions
BeforeEach(func() {
parts = conf.NewInstallElementalParitions()
parts = agentConfig.NewInstallElementalPartitions()
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).ToNot(HaveOccurred())
_, err = fs.Create("/some/device")
Expect(err).ToNot(HaveOccurred())
@ -146,9 +147,9 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var el *elemental.Elemental
var parts v1.ElementalPartitions
BeforeEach(func() {
parts = conf.NewInstallElementalParitions()
parts = agentConfig.NewInstallElementalPartitions()
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).ToNot(HaveOccurred())
_, err = fs.Create("/some/device")
Expect(err).ToNot(HaveOccurred())
@ -194,9 +195,9 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var el *elemental.Elemental
var parts v1.ElementalPartitions
BeforeEach(func() {
parts = conf.NewInstallElementalParitions()
parts = agentConfig.NewInstallElementalPartitions()
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).ToNot(HaveOccurred())
_, err = fs.Create("/some/device")
Expect(err).ToNot(HaveOccurred())
@ -292,7 +293,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
MountPoint: cnst.ActiveDir,
Source: v1.NewDirSrc(cnst.IsoBaseTree),
}
_ = utils.MkdirAll(fs, cnst.IsoBaseTree, cnst.DirPerm)
_ = fsutils.MkdirAll(fs, cnst.IsoBaseTree, cnst.DirPerm)
el = elemental.NewElemental(config)
})
@ -341,10 +342,10 @@ var _ = Describe("Elemental", Label("elemental"), func() {
cInit = &v1mock.FakeCloudInitRunner{ExecStages: []string{}, Error: false}
config.CloudInitRunner = cInit
el = elemental.NewElemental(config)
install = conf.NewInstallSpec(config)
install = agentConfig.NewInstallSpec(config)
install.Target = "/some/device"
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).ToNot(HaveOccurred())
_, err = fs.Create("/some/device")
Expect(err).ToNot(HaveOccurred())
@ -355,7 +356,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var efiPartCmds, partCmds, biosPartCmds [][]string
BeforeEach(func() {
partNum, printOut = 0, printOutput
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).To(BeNil())
efiPartCmds = [][]string{
{
@ -435,7 +436,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
Describe("Run with failures", func() {
var runFunc func(cmd string, args ...string) ([]byte, error)
BeforeEach(func() {
err := utils.MkdirAll(fs, "/some", cnst.DirPerm)
err := fsutils.MkdirAll(fs, "/some", cnst.DirPerm)
Expect(err).To(BeNil())
partNum, printOut = 0, printOutput
runFunc = func(cmd string, args ...string) ([]byte, error) {
@ -486,9 +487,9 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var img *v1.Image
var cmdFail string
BeforeEach(func() {
sourceDir, err := utils.TempDir(fs, "", "elemental")
sourceDir, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).ShouldNot(HaveOccurred())
destDir, err := utils.TempDir(fs, "", "elemental")
destDir, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).ShouldNot(HaveOccurred())
cmdFail = ""
el = elemental.NewElemental(config)
@ -532,7 +533,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
sourceImg := "/source.img"
_, err := fs.Create(sourceImg)
Expect(err).To(BeNil())
destDir, err := utils.TempDir(fs, "", "elemental")
destDir, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).To(BeNil())
img.Source = v1.NewFileSrc(sourceImg)
img.MountPoint = destDir
@ -542,7 +543,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
sourceImg := "/source.img"
_, err := fs.Create(sourceImg)
Expect(err).To(BeNil())
destDir, err := utils.TempDir(fs, "", "elemental")
destDir, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).To(BeNil())
img.Source = v1.NewFileSrc(sourceImg)
img.MountPoint = destDir
@ -554,7 +555,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
sourceImg := "/source.img"
_, err := fs.Create(sourceImg)
Expect(err).To(BeNil())
destDir, err := utils.TempDir(fs, "", "elemental")
destDir, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).To(BeNil())
img.Source = v1.NewFileSrc(sourceImg)
img.MountPoint = destDir
@ -596,7 +597,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
BeforeEach(func() {
var err error
e = elemental.NewElemental(config)
destDir, err = utils.TempDir(fs, "", "elemental")
destDir, err = fsutils.TempDir(fs, "", "elemental")
Expect(err).ShouldNot(HaveOccurred())
})
It("Copies files from a directory source", func() {
@ -682,7 +683,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
var relabelCmd []string
BeforeEach(func() {
// to mock the existance of setfiles command on non selinux hosts
err := utils.MkdirAll(fs, "/usr/sbin", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/usr/sbin", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
sbin, err := fs.RawPath("/usr/sbin")
Expect(err).ShouldNot(HaveOccurred())
@ -696,11 +697,11 @@ var _ = Describe("Elemental", Label("elemental"), func() {
// to mock SELinux policy files
policyFile = filepath.Join(constants.SELinuxTargetedPolicyPath, "policy.31")
err = utils.MkdirAll(fs, filepath.Dir(constants.SELinuxTargetedContextFile), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(constants.SELinuxTargetedContextFile), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(constants.SELinuxTargetedContextFile)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, constants.SELinuxTargetedPolicyPath, constants.DirPerm)
err = fsutils.MkdirAll(fs, constants.SELinuxTargetedPolicyPath, constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(policyFile)
Expect(err).ShouldNot(HaveOccurred())
@ -749,12 +750,12 @@ var _ = Describe("Elemental", Label("elemental"), func() {
})
It("relabels the given root-tree path", func() {
contextFile := filepath.Join("/root", constants.SELinuxTargetedContextFile)
err := utils.MkdirAll(fs, filepath.Dir(contextFile), constants.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Dir(contextFile), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(contextFile)
Expect(err).ShouldNot(HaveOccurred())
policyFile = filepath.Join("/root", policyFile)
err = utils.MkdirAll(fs, filepath.Join("/root", constants.SELinuxTargetedPolicyPath), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Join("/root", constants.SELinuxTargetedPolicyPath), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create(policyFile)
Expect(err).ShouldNot(HaveOccurred())
@ -774,7 +775,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
e = elemental.NewElemental(config)
})
It("Gets the iso and returns the temporary where it is stored", func() {
tmpDir, err := utils.TempDir(fs, "", "elemental-test")
tmpDir, err := fsutils.TempDir(fs, "", "elemental-test")
Expect(err).To(BeNil())
err = fs.WriteFile(fmt.Sprintf("%s/fake.iso", tmpDir), []byte("Hi"), cnst.FilePerm)
Expect(err).To(BeNil())
@ -782,7 +783,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
isoDir, err := e.GetIso(iso)
Expect(err).To(BeNil())
// Confirm that the iso is stored in isoDir
utils.Exists(fs, filepath.Join(isoDir, "cOs.iso"))
fsutils.Exists(fs, filepath.Join(isoDir, "cOs.iso"))
})
It("Fails if it cant find the iso", func() {
iso := "whatever"
@ -792,7 +793,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
})
It("Fails if it cannot mount the iso", func() {
mounter.ErrorOnMount = true
tmpDir, err := utils.TempDir(fs, "", "elemental-test")
tmpDir, err := fsutils.TempDir(fs, "", "elemental-test")
Expect(err).To(BeNil())
err = fs.WriteFile(fmt.Sprintf("%s/fake.iso", tmpDir), []byte("Hi"), cnst.FilePerm)
Expect(err).To(BeNil())
@ -831,7 +832,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
It("updates recovery only image", func() {
recoveryImg = &v1.Image{}
isoMnt := "/some/dir/iso"
err := utils.MkdirAll(fs, isoMnt, cnst.DirPerm)
err := fsutils.MkdirAll(fs, isoMnt, cnst.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
recoverySquash := filepath.Join(isoMnt, cnst.RecoverySquashFile)
_, err = fs.Create(recoverySquash)
@ -883,7 +884,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
Expect(runner.CmdsMatch([][]string{{"grub2-editenv"}})).NotTo(BeNil())
})
It("loads /etc/os-release on empty default entry", func() {
err := utils.MkdirAll(config.Fs, "/imgMountPoint/etc", constants.DirPerm)
err := fsutils.MkdirAll(config.Fs, "/imgMountPoint/etc", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = config.Fs.WriteFile("/imgMountPoint/etc/os-release", []byte("GRUB_ENTRY_NAME=test"), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
@ -904,7 +905,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
})
Describe("FindKernelInitrd", Label("find"), func() {
BeforeEach(func() {
err := utils.MkdirAll(fs, "/path/boot", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/path/boot", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
})
It("finds kernel and initrd files", func() {

View File

@ -1,29 +0,0 @@
/*
Copyright © 2022 SUSE LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package elementalConfig_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestTypes(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "config initializer test suite")
}

View File

@ -19,13 +19,14 @@ package partitioner
import (
"errors"
"fmt"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/partitions"
"os"
"regexp"
"strings"
"time"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
"github.com/twpayne/go-vfs"
)
@ -327,7 +328,7 @@ func (dev Disk) FindPartitionDevice(partNum int) (string, error) {
for tries := 0; tries <= partitionTries; tries++ {
dev.logger.Debugf("Trying to find the partition device %d of device %s (try number %d)", partNum, dev, tries+1)
_, _ = dev.runner.Run("udevadm", "settle")
if exists, _ := utils.Exists(dev.fs, device); exists {
if exists, _ := fsutils.Exists(dev.fs, device); exists {
return device, nil
}
time.Sleep(1 * time.Second)
@ -401,7 +402,7 @@ func (dev Disk) expandFilesystem(device string) (string, error) {
var out []byte
var err error
fs, err := utils.GetPartitionFS(device)
fs, err := partitions.GetPartitionFS(device)
if err != nil {
return fs, err
}
@ -419,7 +420,7 @@ func (dev Disk) expandFilesystem(device string) (string, error) {
}
case "xfs":
// to grow an xfs fs it needs to be mounted :/
tmpDir, err := utils.TempDir(dev.fs, "", "partitioner")
tmpDir, err := fsutils.TempDir(dev.fs, "", "partitioner")
defer func(fs v1.FS, path string) {
_ = fs.RemoveAll(path)
}(dev.fs, tmpDir)

View File

@ -18,13 +18,13 @@ package partitioner_test
import (
"errors"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"testing"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
part "github.com/kairos-io/kairos-agent/v2/pkg/partitioner"
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
mocks "github.com/kairos-io/kairos-agent/v2/tests/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -211,7 +211,7 @@ var _ = Describe("Partitioner", Label("disk", "partition", "partitioner"), func(
BeforeEach(func() {
fs, cleanup, _ = vfst.NewTestFS(nil)
err := utils.MkdirAll(fs, "/dev", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/dev", constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create("/dev/device")
Expect(err).To(BeNil())

View File

@ -19,12 +19,10 @@ package v1
import (
"fmt"
"path/filepath"
"runtime"
"sort"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
"gopkg.in/yaml.v3"
"k8s.io/mount-utils"
)
const (
@ -43,94 +41,6 @@ type Spec interface {
ShouldShutdown() bool
}
// Config is the struct that includes basic and generic configuration of elemental binary runtime.
// It mostly includes the interfaces used around many methods in elemental code
type Config struct {
Debug bool `yaml:"debug,omitempty" mapstructure:"debug"`
Strict bool `yaml:"strict,omitempty" mapstructure:"strict"`
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
Logger Logger
Fs FS
Mounter mount.Interface
Runner Runner
Syscall SyscallInterface
CloudInitRunner CloudInitRunner
ImageExtractor ImageExtractor
Client HTTPClient
Platform *Platform `yaml:"platform,omitempty" mapstructure:"platform"`
Cosign bool `yaml:"cosign,omitempty" mapstructure:"cosign"`
Verify bool `yaml:"verify,omitempty" mapstructure:"verify"`
CosignPubKey string `yaml:"cosign-key,omitempty" mapstructure:"cosign-key"`
Arch string `yaml:"arch,omitempty" mapstructure:"arch"`
SquashFsCompressionConfig []string `yaml:"squash-compression,omitempty" mapstructure:"squash-compression"`
SquashFsNoCompression bool `yaml:"squash-no-compression,omitempty" mapstructure:"squash-no-compression"`
}
// WriteInstallState writes the state.yaml file to the given state and recovery paths
func (c Config) WriteInstallState(i *InstallState, statePath, recoveryPath string) error {
data, err := yaml.Marshal(i)
if err != nil {
return err
}
data = append([]byte("# Autogenerated file by elemental client, do not edit\n\n"), data...)
err = c.Fs.WriteFile(statePath, data, constants.FilePerm)
if err != nil {
return err
}
err = c.Fs.WriteFile(recoveryPath, data, constants.FilePerm)
if err != nil {
return err
}
return nil
}
// LoadInstallState loads the state.yaml file and unmarshals it to an InstallState object
func (c Config) LoadInstallState() (*InstallState, error) {
installState := &InstallState{}
data, err := c.Fs.ReadFile(filepath.Join(constants.RunningStateDir, constants.InstallStateFile))
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, installState)
if err != nil {
return nil, err
}
return installState, nil
}
// Sanitize checks the consistency of the struct, returns error
// if unsolvable inconsistencies are found
func (c *Config) Sanitize() error {
// If no squashcompression is set, zero the compression parameters
// By default on NewConfig the SquashFsCompressionConfig is set to the default values, and then override
// on config unmarshall.
if c.SquashFsNoCompression {
c.SquashFsCompressionConfig = []string{}
}
if c.Arch != "" {
p, err := NewPlatformFromArch(c.Arch)
if err != nil {
return err
}
c.Platform = p
}
if c.Platform == nil {
p, err := NewPlatformFromArch(runtime.GOARCH)
if err != nil {
return err
}
c.Platform = p
}
return nil
}
// InstallSpec struct represents all the installation action details
type InstallSpec struct {
Target string `yaml:"device,omitempty" mapstructure:"device"`

View File

@ -17,108 +17,13 @@ limitations under the License.
package v1_test
import (
"path/filepath"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
"github.com/kairos-io/kairos-agent/v2/pkg/elementalConfig"
conf "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"
v1mocks "github.com/kairos-io/kairos-agent/v2/tests/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/twpayne/go-vfs"
"github.com/twpayne/go-vfs/vfst"
)
var _ = Describe("Types", Label("types", "config"), func() {
Describe("Write and load installation state", func() {
var config *v1.Config
var runner *v1mocks.FakeRunner
var fs vfs.FS
var mounter *v1mocks.ErrorMounter
var cleanup func()
var err error
var dockerState, channelState *v1.ImageState
var installState *v1.InstallState
var statePath, recoveryPath string
BeforeEach(func() {
runner = v1mocks.NewFakeRunner()
mounter = v1mocks.NewErrorMounter()
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{})
Expect(err).Should(BeNil())
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithMounter(mounter),
)
dockerState = &v1.ImageState{
Source: v1.NewDockerSrc("registry.org/my/image:tag"),
Label: "active_label",
FS: "ext2",
SourceMetadata: &v1.DockerImageMeta{
Digest: "adadgadg",
Size: 23452345,
},
}
installState = &v1.InstallState{
Date: "somedate",
Partitions: map[string]*v1.PartitionState{
"state": {
FSLabel: "state_label",
Images: map[string]*v1.ImageState{
"active": dockerState,
},
},
"recovery": {
FSLabel: "state_label",
Images: map[string]*v1.ImageState{
"recovery": channelState,
},
},
},
}
statePath = filepath.Join(constants.RunningStateDir, constants.InstallStateFile)
recoveryPath = "/recoverypart/state.yaml"
err = utils.MkdirAll(fs, filepath.Dir(recoveryPath), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, filepath.Dir(statePath), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
})
AfterEach(func() {
cleanup()
})
It("Writes and loads an installation data", func() {
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).ShouldNot(HaveOccurred())
loadedInstallState, err := config.LoadInstallState()
Expect(err).ShouldNot(HaveOccurred())
Expect(*loadedInstallState).To(Equal(*installState))
})
It("Fails writing to state partition", func() {
err = fs.RemoveAll(filepath.Dir(statePath))
Expect(err).ShouldNot(HaveOccurred())
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).Should(HaveOccurred())
})
It("Fails writing to recovery partition", func() {
err = fs.RemoveAll(filepath.Dir(statePath))
Expect(err).ShouldNot(HaveOccurred())
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).Should(HaveOccurred())
})
It("Fails loading state file", func() {
err = config.WriteInstallState(installState, statePath, recoveryPath)
Expect(err).ShouldNot(HaveOccurred())
err = fs.RemoveAll(filepath.Dir(statePath))
_, err = config.LoadInstallState()
Expect(err).Should(HaveOccurred())
})
})
Describe("ElementalPartitions", func() {
var p v1.PartitionList
var ep v1.ElementalPartitions
@ -319,151 +224,4 @@ var _ = Describe("Types", Label("types", "config"), func() {
Expect(p.GetByName("nonexistent")).To(BeNil())
})
})
Describe("Config", func() {
It("runs sanitize method", func() {
cfg := elementalConfig.NewConfig(elementalConfig.WithMounter(v1mocks.NewErrorMounter()))
cfg.Verify = true
err := cfg.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
})
})
Describe("InstallSpec", func() {
var spec *v1.InstallSpec
BeforeEach(func() {
cfg := elementalConfig.NewConfig(elementalConfig.WithMounter(v1mocks.NewErrorMounter()))
spec = elementalConfig.NewInstallSpec(cfg)
})
Describe("sanitize", func() {
It("runs method", func() {
Expect(spec.Partitions.EFI).To(BeNil())
Expect(spec.Active.Source.IsEmpty()).To(BeTrue())
// Creates firmware partitions
spec.Active.Source = v1.NewDirSrc("/dir")
spec.Firmware = v1.EFI
err := spec.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Partitions.EFI).NotTo(BeNil())
// Sets recovery image file to squashfs file
spec.Recovery.FS = constants.SquashFs
err = spec.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Recovery.File).To(ContainSubstring(constants.RecoverySquashFile))
// Sets recovery image file to img file
spec.Recovery.FS = constants.LinuxImgFs
err = spec.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
Expect(spec.Recovery.File).To(ContainSubstring(constants.RecoveryImgFile))
// Fails without state partition
spec.Partitions.State = nil
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
// Fails without an install source
spec.Active.Source = v1.NewEmptySrc()
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
})
Describe("with extra partitions", func() {
BeforeEach(func() {
// Set a source for the install
spec.Active.Source = v1.NewDirSrc("/dir")
})
It("fails if persistent and an extra partition have size == 0", func() {
spec.ExtraPartitions = append(spec.ExtraPartitions, &v1.Partition{Size: 0})
err := spec.Sanitize()
Expect(err).Should(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("both persistent partition and extra partitions have size set to 0"))
})
It("fails if more than one extra partition has size == 0", func() {
spec.Partitions.Persistent.Size = 10
spec.ExtraPartitions = append(spec.ExtraPartitions, &v1.Partition{Name: "1", Size: 0})
spec.ExtraPartitions = append(spec.ExtraPartitions, &v1.Partition{Name: "2", Size: 0})
err := spec.Sanitize()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("more than one extra partition has its size set to 0"))
})
It("does not fail if persistent size is > 0 and an extra partition has size == 0", func() {
spec.ExtraPartitions = append(spec.ExtraPartitions, &v1.Partition{Size: 0})
spec.Partitions.Persistent.Size = 10
err := spec.Sanitize()
Expect(err).ToNot(HaveOccurred())
})
})
})
})
Describe("ResetSpec", func() {
It("runs sanitize method", func() {
spec := &v1.ResetSpec{
Active: v1.Image{
Source: v1.NewDirSrc("/dir"),
},
Partitions: v1.ElementalPartitions{
State: &v1.Partition{
MountPoint: "mountpoint",
},
},
}
err := spec.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
//Fails on missing state partition
spec.Partitions.State = nil
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
//Fails on empty source
spec.Active.Source = v1.NewEmptySrc()
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
})
})
Describe("UpgradeSpec", func() {
It("runs sanitize method", func() {
spec := &v1.UpgradeSpec{
Active: v1.Image{
Source: v1.NewDirSrc("/dir"),
},
Recovery: v1.Image{
Source: v1.NewDirSrc("/dir"),
},
Partitions: v1.ElementalPartitions{
State: &v1.Partition{
MountPoint: "mountpoint",
},
Recovery: &v1.Partition{
MountPoint: "mountpoint",
},
},
}
err := spec.Sanitize()
Expect(err).ShouldNot(HaveOccurred())
//Fails on empty source for active upgrade
spec.Active.Source = v1.NewEmptySrc()
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
//Fails on missing state partition for active upgrade
spec.Partitions.State = nil
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
//Fails on empty source for recovery upgrade
spec.RecoveryUpgrade = true
spec.Recovery.Source = v1.NewEmptySrc()
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
//Fails on missing recovery partition for recovery upgrade
spec.Partitions.Recovery = nil
err = spec.Sanitize()
Expect(err).Should(HaveOccurred())
})
})
})

View File

@ -19,12 +19,13 @@ package utils
import (
"errors"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"os"
"sort"
"strings"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
)
// Chroot represents the struct that will allow us to run commands inside a given chroot
@ -33,10 +34,10 @@ type Chroot struct {
defaultMounts []string
extraMounts map[string]string
activeMounts []string
config *v1.Config
config *agentConfig.Config
}
func NewChroot(path string, config *v1.Config) *Chroot {
func NewChroot(path string, config *agentConfig.Config) *Chroot {
return &Chroot{
path: path,
defaultMounts: []string{"/dev", "/dev/pts", "/proc", "/sys"},
@ -47,7 +48,7 @@ func NewChroot(path string, config *v1.Config) *Chroot {
}
// ChrootedCallback runs the given callback in a chroot environment
func ChrootedCallback(cfg *v1.Config, path string, bindMounts map[string]string, callback func() error) error {
func ChrootedCallback(cfg *agentConfig.Config, path string, bindMounts map[string]string, callback func() error) error {
chroot := NewChroot(path, cfg)
chroot.SetExtraMounts(bindMounts)
return chroot.RunCallback(callback)
@ -78,7 +79,7 @@ func (c *Chroot) Prepare() error {
for _, mnt := range c.defaultMounts {
mountPoint := fmt.Sprintf("%s%s", strings.TrimSuffix(c.path, "/"), mnt)
err = MkdirAll(c.config.Fs, mountPoint, constants.DirPerm)
err = fsutils.MkdirAll(c.config.Fs, mountPoint, constants.DirPerm)
if err != nil {
return err
}
@ -95,7 +96,7 @@ func (c *Chroot) Prepare() error {
sort.Strings(keys)
for _, k := range keys {
mountPoint := fmt.Sprintf("%s%s", strings.TrimSuffix(c.path, "/"), c.extraMounts[k])
err = MkdirAll(c.config.Fs, mountPoint, constants.DirPerm)
err = fsutils.MkdirAll(c.config.Fs, mountPoint, constants.DirPerm)
if err != nil {
return err
}

View File

@ -20,6 +20,9 @@ import (
"crypto/sha256"
"errors"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/partitions"
"io"
random "math/rand"
"net/url"
@ -42,12 +45,6 @@ func CommandExists(command string) bool {
return err == nil
}
// BootedFrom will check if we are booting from the given label
func BootedFrom(runner v1.Runner, label string) bool {
out, _ := runner.Run("cat", "/proc/cmdline")
return strings.Contains(string(out), label)
}
// GetDeviceByLabel will try to return the device that matches the given label.
// attempts value sets the number of attempts to find the device, it
// waits a second between attempts.
@ -64,7 +61,7 @@ func GetDeviceByLabel(runner v1.Runner, label string, attempts int) (string, err
func GetFullDeviceByLabel(runner v1.Runner, label string, attempts int) (*v1.Partition, error) {
for tries := 0; tries < attempts; tries++ {
_, _ = runner.Run("udevadm", "settle")
parts, err := GetAllPartitions()
parts, err := partitions.GetAllPartitions()
if err != nil {
return nil, err
}
@ -91,7 +88,7 @@ func ConcatFiles(fs v1.FS, sources []string, target string) (err error) {
if len(sources) == 0 {
return fmt.Errorf("Empty sources list")
}
if dir, _ := IsDir(fs, target); dir {
if dir, _ := fsutils.IsDir(fs, target); dir {
target = filepath.Join(target, filepath.Base(sources[0]))
}
@ -129,18 +126,18 @@ func ConcatFiles(fs v1.FS, sources []string, target string) (err error) {
// Copies source file to target file using Fs interface
func CreateDirStructure(fs v1.FS, target string) error {
for _, dir := range []string{"/run", "/dev", "/boot", "/usr/local", "/oem"} {
err := MkdirAll(fs, filepath.Join(target, dir), cnst.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(target, dir), cnst.DirPerm)
if err != nil {
return err
}
}
for _, dir := range []string{"/proc", "/sys"} {
err := MkdirAll(fs, filepath.Join(target, dir), cnst.NoWriteDirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(target, dir), cnst.NoWriteDirPerm)
if err != nil {
return err
}
}
err := MkdirAll(fs, filepath.Join(target, "/tmp"), cnst.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(target, "/tmp"), cnst.DirPerm)
if err != nil {
return err
}
@ -245,7 +242,7 @@ func CosignVerify(fs v1.FS, runner v1.Runner, image string, publicKey string, de
args = append(args, image)
// Give each cosign its own tuf dir so it doesnt collide with others accessing the same files at the same time
tmpDir, err := TempDir(fs, "", "cosign-tuf-")
tmpDir, err := fsutils.TempDir(fs, "", "cosign-tuf-")
if err != nil {
return "", err
}
@ -301,7 +298,7 @@ func LoadEnvFile(fs v1.FS, file string) (map[string]string, error) {
return envMap, err
}
func IsMounted(config *v1.Config, part *v1.Partition) (bool, error) {
func IsMounted(config *agentConfig.Config, part *v1.Partition) (bool, error) {
if part == nil {
return false, fmt.Errorf("nil partition")
}
@ -318,37 +315,11 @@ func IsMounted(config *v1.Config, part *v1.Partition) (bool, error) {
return !notMnt, nil
}
// HasSquashedRecovery returns true if a squashed recovery image is found in the system
func HasSquashedRecovery(config *v1.Config, recovery *v1.Partition) (squashed bool, err error) {
mountPoint := recovery.MountPoint
if mnt, _ := IsMounted(config, recovery); !mnt {
tmpMountDir, err := TempDir(config.Fs, "", "elemental")
if err != nil {
config.Logger.Errorf("failed creating temporary dir: %v", err)
return false, err
}
defer config.Fs.RemoveAll(tmpMountDir) // nolint:errcheck
err = config.Mounter.Mount(recovery.Path, tmpMountDir, "auto", []string{})
if err != nil {
config.Logger.Errorf("failed mounting recovery partition: %v", err)
return false, err
}
mountPoint = tmpMountDir
defer func() {
err = config.Mounter.Unmount(tmpMountDir)
if err != nil {
squashed = false
}
}()
}
return Exists(config.Fs, filepath.Join(mountPoint, "cOS", cnst.RecoverySquashFile))
}
// GetTempDir returns the dir for storing related temporal files
// It will respect TMPDIR and use that if exists, fallback to try the persistent partition if its mounted
// and finally the default /tmp/ dir
// suffix is what is appended to the dir name elemental-suffix. If empty it will randomly generate a number
func GetTempDir(config *v1.Config, suffix string) string {
func GetTempDir(config *agentConfig.Config, suffix string) string {
// if we got a TMPDIR var, respect and use that
if suffix == "" {
random.Seed(time.Now().UnixNano())
@ -360,7 +331,7 @@ func GetTempDir(config *v1.Config, suffix string) string {
config.Logger.Debugf("Got tmpdir from TMPDIR var: %s", dir)
return filepath.Join(dir, elementalTmpDir)
}
parts, err := GetAllPartitions()
parts, err := partitions.GetAllPartitions()
if err != nil {
config.Logger.Debug("Could not get partitions, defaulting to /tmp")
return filepath.Join("/", "tmp", elementalTmpDir)
@ -414,7 +385,7 @@ func IsHTTPURI(uri string) (bool, error) {
// GetSource copies given source to destination, if source is a local path it simply
// copies files, if source is a remote URL it tries to download URL to destination.
func GetSource(config *v1.Config, source string, destination string) error {
func GetSource(config *agentConfig.Config, source string, destination string) error {
local, err := IsLocalURI(source)
if err != nil {
config.Logger.Errorf("Not a valid url: %s", source)
@ -484,7 +455,7 @@ func FindFileWithPrefix(fs v1.FS, path string, prefixes ...string) (string, erro
if !filepath.IsAbs(found) {
found = filepath.Join(path, found)
}
if exists, _ := Exists(fs, found); exists {
if exists, _ := fsutils.Exists(fs, found); exists {
return found, nil
}
}
@ -497,19 +468,6 @@ func FindFileWithPrefix(fs v1.FS, path string, prefixes ...string) (string, erro
return "", fmt.Errorf("No file found with prefixes: %v", prefixes)
}
var errInvalidArch = fmt.Errorf("invalid arch")
func GolangArchToArch(arch string) (string, error) {
switch strings.ToLower(arch) {
case cnst.ArchAmd64:
return cnst.Archx86, nil
case cnst.ArchArm64:
return cnst.ArchArm64, nil
default:
return "", errInvalidArch
}
}
// CalcFileChecksum opens the given file and returns the sha256 checksum of it.
func CalcFileChecksum(fs v1.FS, fileName string) (string, error) {
f, err := fs.Open(fileName)

View File

@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package utils
package fsutils
import (
"io/fs"

View File

@ -18,21 +18,22 @@ package utils
import (
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"io/fs"
"path/filepath"
"strings"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
)
// Grub is the struct that will allow us to install grub to the target device
type Grub struct {
config *v1.Config
config *agentConfig.Config
}
func NewGrub(config *v1.Config) *Grub {
func NewGrub(config *agentConfig.Config) *Grub {
g := &Grub{
config: config,
}
@ -71,7 +72,7 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
// Select the proper dir for grub - this assumes that we previously run a grub install command, which is not the case in EFI
// In the EFI case we default to grub2
if ok, _ := IsDir(g.config.Fs, filepath.Join(bootDir, "grub")); ok {
if ok, _ := fsutils.IsDir(g.config.Fs, filepath.Join(bootDir, "grub")); ok {
systemgrub = "grub"
}
}
@ -86,7 +87,7 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
}
// Create Needed dir under state partition to store the grub.cfg and any needed modules
err = MkdirAll(g.config.Fs, filepath.Join(bootDir, fmt.Sprintf("%s/%s-efi", systemgrub, g.config.Arch)), cnst.DirPerm)
err = fsutils.MkdirAll(g.config.Fs, filepath.Join(bootDir, fmt.Sprintf("%s/%s-efi", systemgrub, g.config.Arch)), cnst.DirPerm)
if err != nil {
return fmt.Errorf("error creating grub dir: %s", err)
}
@ -108,7 +109,7 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
}
}
ttyExists, _ := Exists(g.config.Fs, fmt.Sprintf("/dev/%s", tty))
ttyExists, _ := fsutils.Exists(g.config.Fs, fmt.Sprintf("/dev/%s", tty))
if ttyExists && tty != "" && tty != "console" && tty != constants.DefaultTty {
// We need to add a tty to the grub file
@ -134,7 +135,7 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
g.config.Logger.Infof("Generating grub files for efi on %s", target)
var foundModules bool
for _, m := range []string{"loopback.mod", "squash4.mod", "xzio.mod", "gzio.mod"} {
err = WalkDirFs(g.config.Fs, rootDir, func(path string, d fs.DirEntry, err error) error {
err = fsutils.WalkDirFs(g.config.Fs, rootDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
@ -159,7 +160,7 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
}
}
err = MkdirAll(g.config.Fs, filepath.Join(cnst.EfiDir, "EFI/boot/"), cnst.DirPerm)
err = fsutils.MkdirAll(g.config.Fs, filepath.Join(cnst.EfiDir, "EFI/boot/"), cnst.DirPerm)
if err != nil {
g.config.Logger.Errorf("Error creating dirs: %s", err)
return err

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package utils
package partitions
import (
"fmt"

View File

@ -18,11 +18,12 @@ package utils
import (
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
"github.com/mudler/yip/pkg/schema"
"gopkg.in/yaml.v3"
)
@ -43,7 +44,7 @@ func onlyYAMLPartialErrors(er error) bool {
return true
}
func checkYAMLError(cfg *v1.Config, allErrors, err error) error {
func checkYAMLError(cfg *agentConfig.Config, allErrors, err error) error {
if !onlyYAMLPartialErrors(err) {
// here we absorb errors only if are related to YAML unmarshalling
// As cmdline is parsed out as a yaml file
@ -57,7 +58,7 @@ func checkYAMLError(cfg *v1.Config, allErrors, err error) error {
}
// RunStage will run yip
func RunStage(cfg *v1.Config, stage string) error {
func RunStage(cfg *agentConfig.Config, stage string) error {
var cmdLineYipURI string
var allErrors error
var cloudInitPaths []string
@ -67,7 +68,7 @@ func RunStage(cfg *v1.Config, stage string) error {
// Make sure cloud init path specified are existing in the system
for _, cp := range cloudInitPaths {
err := MkdirAll(cfg.Fs, cp, constants.DirPerm)
err := fsutils.MkdirAll(cfg.Fs, cp, constants.DirPerm)
if err != nil {
cfg.Logger.Debugf("Failed creating cloud-init config path: %s %s", cp, err.Error())
}

View File

@ -19,10 +19,11 @@ package utils_test
import (
"bytes"
"fmt"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"os"
"github.com/kairos-io/kairos-agent/v2/pkg/cloudinit"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
@ -41,7 +42,7 @@ func writeCmdline(s string, fs v1.FS) error {
}
var _ = Describe("run stage", Label("RunStage"), func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var logger v1.Logger
var syscall *v1mock.FakeSyscall
@ -61,13 +62,13 @@ var _ = Describe("run stage", Label("RunStage"), func() {
logger.SetLevel(log.DebugLevel)
fs, cleanup, _ = vfst.NewTestFS(nil)
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
)
config.CloudInitRunner = cloudinit.NewYipCloudInitRunner(config.Logger, config.Runner, fs)
@ -75,7 +76,7 @@ var _ = Describe("run stage", Label("RunStage"), func() {
AfterEach(func() { cleanup() })
It("fails if strict mode is enabled", Label("strict"), func() {
d, err := utils.TempDir(fs, "", "elemental")
d, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).ToNot(HaveOccurred())
_ = fs.WriteFile(fmt.Sprintf("%s/test.yaml", d), []byte("stages: [foo,bar]"), os.ModePerm)
config.Strict = true
@ -92,7 +93,7 @@ var _ = Describe("run stage", Label("RunStage"), func() {
})
It("Goes over extra paths", func() {
d, err := utils.TempDir(fs, "", "elemental")
d, err := fsutils.TempDir(fs, "", "elemental")
Expect(err).ToNot(HaveOccurred())
config.Logger.SetLevel(log.DebugLevel)
config.CloudInitPaths = []string{d}
@ -105,7 +106,7 @@ var _ = Describe("run stage", Label("RunStage"), func() {
})
It("parses cmdline uri", func() {
d, _ := utils.TempDir(fs, "", "elemental")
d, _ := fsutils.TempDir(fs, "", "elemental")
_ = fs.WriteFile(fmt.Sprintf("%s/test.yaml", d), []byte{}, os.ModePerm)
writeCmdline(fmt.Sprintf("cos.setup=%s/test.yaml", d), fs)

View File

@ -20,6 +20,8 @@ import (
"bytes"
"errors"
"fmt"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/kairos-io/kairos-agent/v2/pkg/utils/partitions"
"os"
"path/filepath"
"strings"
@ -27,8 +29,8 @@ import (
"github.com/jaypipes/ghw/pkg/block"
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
conf "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"
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
@ -47,7 +49,7 @@ func getNamesFromListFiles(list []os.FileInfo) []string {
}
var _ = Describe("Utils", Label("utils"), func() {
var config *v1.Config
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var logger v1.Logger
var syscall *v1mock.FakeSyscall
@ -70,13 +72,13 @@ var _ = Describe("Utils", Label("utils"), func() {
fs.Mkdir("/run", constants.DirPerm)
fs.Mkdir("/etc", constants.DirPerm)
config = conf.NewConfig(
conf.WithFs(fs),
conf.WithRunner(runner),
conf.WithLogger(logger),
conf.WithMounter(mounter),
conf.WithSyscall(syscall),
conf.WithClient(client),
config = agentConfig.NewConfig(
agentConfig.WithFs(fs),
agentConfig.WithRunner(runner),
agentConfig.WithLogger(logger),
agentConfig.WithMounter(mounter),
agentConfig.WithSyscall(syscall),
agentConfig.WithClient(client),
)
})
AfterEach(func() { cleanup() })
@ -176,16 +178,6 @@ var _ = Describe("Utils", Label("utils"), func() {
})
})
})
Describe("TestBootedFrom", Label("BootedFrom"), func() {
It("returns true if we are booting from label FAKELABEL", func() {
runner.ReturnValue = []byte("")
Expect(utils.BootedFrom(runner, "FAKELABEL")).To(BeFalse())
})
It("returns false if we are not booting from label FAKELABEL", func() {
runner.ReturnValue = []byte("FAKELABEL")
Expect(utils.BootedFrom(runner, "FAKELABEL")).To(BeTrue())
})
})
Describe("GetDeviceByLabel", Label("lsblk", "partitions"), func() {
var cmds [][]string
BeforeEach(func() {
@ -246,7 +238,7 @@ var _ = Describe("Utils", Label("utils"), func() {
ghwTest.Clean()
})
It("returns all found partitions", func() {
parts, err := utils.GetAllPartitions()
parts, err := partitions.GetAllPartitions()
Expect(err).To(BeNil())
var partNames []string
for _, p := range parts {
@ -277,17 +269,17 @@ var _ = Describe("Utils", Label("utils"), func() {
ghwTest.Clean()
})
It("returns found device with plain partition device", func() {
pFS, err := utils.GetPartitionFS("device1")
pFS, err := partitions.GetPartitionFS("device1")
Expect(err).To(BeNil())
Expect(pFS).To(Equal("xfs"))
})
It("returns found device with full partition device", func() {
pFS, err := utils.GetPartitionFS("/dev/device1")
pFS, err := partitions.GetPartitionFS("/dev/device1")
Expect(err).To(BeNil())
Expect(pFS).To(Equal("xfs"))
})
It("fails if no partition is found", func() {
_, err := utils.GetPartitionFS("device2")
_, err := partitions.GetPartitionFS("device2")
Expect(err).NotTo(BeNil())
})
})
@ -377,40 +369,40 @@ var _ = Describe("Utils", Label("utils"), func() {
})
Describe("CopyFile", Label("CopyFile"), func() {
It("Copies source file to target file", func() {
err := utils.MkdirAll(fs, "/some", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/some", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create("/some/file")
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Stat("/some/otherfile")
Expect(err).Should(HaveOccurred())
Expect(utils.CopyFile(fs, "/some/file", "/some/otherfile")).ShouldNot(HaveOccurred())
e, err := utils.Exists(fs, "/some/otherfile")
e, err := fsutils.Exists(fs, "/some/otherfile")
Expect(err).ShouldNot(HaveOccurred())
Expect(e).To(BeTrue())
})
It("Copies source file to target folder", func() {
err := utils.MkdirAll(fs, "/some", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/some", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, "/someotherfolder", constants.DirPerm)
err = fsutils.MkdirAll(fs, "/someotherfolder", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create("/some/file")
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Stat("/someotherfolder/file")
Expect(err).Should(HaveOccurred())
Expect(utils.CopyFile(fs, "/some/file", "/someotherfolder")).ShouldNot(HaveOccurred())
e, err := utils.Exists(fs, "/someotherfolder/file")
e, err := fsutils.Exists(fs, "/someotherfolder/file")
Expect(err).ShouldNot(HaveOccurred())
Expect(e).To(BeTrue())
})
It("Fails to open non existing file", func() {
err := utils.MkdirAll(fs, "/some", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/some", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
Expect(utils.CopyFile(fs, "/some/file", "/some/otherfile")).NotTo(BeNil())
_, err = fs.Stat("/some/otherfile")
Expect(err).NotTo(BeNil())
})
It("Fails to copy on non writable target", func() {
err := utils.MkdirAll(fs, "/some", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/some", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
fs.Create("/some/file")
_, err = fs.Stat("/some/otherfile")
@ -448,13 +440,13 @@ var _ = Describe("Utils", Label("utils"), func() {
})
Describe("SyncData", Label("SyncData"), func() {
It("Copies all files from source to target", func() {
sourceDir, err := utils.TempDir(fs, "", "elementalsource")
sourceDir, err := fsutils.TempDir(fs, "", "elementalsource")
Expect(err).ShouldNot(HaveOccurred())
destDir, err := utils.TempDir(fs, "", "elementaltarget")
destDir, err := fsutils.TempDir(fs, "", "elementaltarget")
Expect(err).ShouldNot(HaveOccurred())
for i := 0; i < 5; i++ {
_, _ = utils.TempFile(fs, sourceDir, "file*")
_, _ = fsutils.TempFile(fs, sourceDir, "file*")
}
Expect(utils.SyncData(logger, realRunner, fs, sourceDir, destDir)).To(BeNil())
@ -473,19 +465,19 @@ var _ = Describe("Utils", Label("utils"), func() {
})
It("Copies all files from source to target respecting excludes", func() {
sourceDir, err := utils.TempDir(fs, "", "elementalsource")
sourceDir, err := fsutils.TempDir(fs, "", "elementalsource")
Expect(err).ShouldNot(HaveOccurred())
destDir, err := utils.TempDir(fs, "", "elementaltarget")
destDir, err := fsutils.TempDir(fs, "", "elementaltarget")
Expect(err).ShouldNot(HaveOccurred())
utils.MkdirAll(fs, filepath.Join(sourceDir, "host"), constants.DirPerm)
utils.MkdirAll(fs, filepath.Join(sourceDir, "run"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "host"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "run"), constants.DirPerm)
// /tmp/run would be excluded as well, as we define an exclude without the "/" prefix
utils.MkdirAll(fs, filepath.Join(sourceDir, "tmp", "run"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "tmp", "run"), constants.DirPerm)
for i := 0; i < 5; i++ {
_, _ = utils.TempFile(fs, sourceDir, "file*")
_, _ = fsutils.TempFile(fs, sourceDir, "file*")
}
Expect(utils.SyncData(logger, realRunner, fs, sourceDir, destDir, "host", "run")).To(BeNil())
@ -512,19 +504,19 @@ var _ = Describe("Utils", Label("utils"), func() {
Expect(destNames).To(Equal(expected))
// /tmp/run is not copied over
Expect(utils.Exists(fs, filepath.Join(destDir, "tmp", "run"))).To(BeFalse())
Expect(fsutils.Exists(fs, filepath.Join(destDir, "tmp", "run"))).To(BeFalse())
})
It("Copies all files from source to target respecting excludes with '/' prefix", func() {
sourceDir, err := utils.TempDir(fs, "", "elementalsource")
sourceDir, err := fsutils.TempDir(fs, "", "elementalsource")
Expect(err).ShouldNot(HaveOccurred())
destDir, err := utils.TempDir(fs, "", "elementaltarget")
destDir, err := fsutils.TempDir(fs, "", "elementaltarget")
Expect(err).ShouldNot(HaveOccurred())
utils.MkdirAll(fs, filepath.Join(sourceDir, "host"), constants.DirPerm)
utils.MkdirAll(fs, filepath.Join(sourceDir, "run"), constants.DirPerm)
utils.MkdirAll(fs, filepath.Join(sourceDir, "var", "run"), constants.DirPerm)
utils.MkdirAll(fs, filepath.Join(sourceDir, "tmp", "host"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "host"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "run"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "var", "run"), constants.DirPerm)
fsutils.MkdirAll(fs, filepath.Join(sourceDir, "tmp", "host"), constants.DirPerm)
Expect(utils.SyncData(logger, realRunner, fs, sourceDir, destDir, "/host", "/run")).To(BeNil())
@ -541,16 +533,16 @@ var _ = Describe("Utils", Label("utils"), func() {
// Shouldn't be the same
Expect(destNames).ToNot(Equal(SourceNames))
Expect(utils.Exists(fs, filepath.Join(destDir, "var", "run"))).To(BeTrue())
Expect(utils.Exists(fs, filepath.Join(destDir, "tmp", "host"))).To(BeTrue())
Expect(utils.Exists(fs, filepath.Join(destDir, "host"))).To(BeFalse())
Expect(utils.Exists(fs, filepath.Join(destDir, "run"))).To(BeFalse())
Expect(fsutils.Exists(fs, filepath.Join(destDir, "var", "run"))).To(BeTrue())
Expect(fsutils.Exists(fs, filepath.Join(destDir, "tmp", "host"))).To(BeTrue())
Expect(fsutils.Exists(fs, filepath.Join(destDir, "host"))).To(BeFalse())
Expect(fsutils.Exists(fs, filepath.Join(destDir, "run"))).To(BeFalse())
})
It("should not fail if dirs are empty", func() {
sourceDir, err := utils.TempDir(fs, "", "elementalsource")
sourceDir, err := fsutils.TempDir(fs, "", "elementalsource")
Expect(err).ShouldNot(HaveOccurred())
destDir, err := utils.TempDir(fs, "", "elementaltarget")
destDir, err := fsutils.TempDir(fs, "", "elementaltarget")
Expect(err).ShouldNot(HaveOccurred())
Expect(utils.SyncData(logger, realRunner, fs, sourceDir, destDir)).To(BeNil())
})
@ -672,7 +664,7 @@ var _ = Describe("Utils", Label("utils"), func() {
})
Describe("DirSize", Label("fs"), func() {
BeforeEach(func() {
err := utils.MkdirAll(fs, "/folder/subfolder", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/folder/subfolder", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
f, err := fs.Create("/folder/file")
Expect(err).ShouldNot(HaveOccurred())
@ -684,14 +676,14 @@ var _ = Describe("Utils", Label("utils"), func() {
Expect(err).ShouldNot(HaveOccurred())
})
It("Returns the expected size of a test folder", func() {
size, err := utils.DirSize(fs, "/folder")
size, err := fsutils.DirSize(fs, "/folder")
Expect(err).ShouldNot(HaveOccurred())
Expect(size).To(Equal(int64(3072)))
})
})
Describe("FindFileWithPrefix", Label("find"), func() {
BeforeEach(func() {
err := utils.MkdirAll(fs, "/path/inner", constants.DirPerm)
err := fsutils.MkdirAll(fs, "/path/inner", constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = fs.Create("/path/onefile")
@ -740,7 +732,7 @@ var _ = Describe("Utils", Label("utils"), func() {
Expect(err).Should(HaveOccurred())
})
It("doesn't find any matching file in path", func() {
utils.MkdirAll(fs, "/path", constants.DirPerm)
fsutils.MkdirAll(fs, "/path", constants.DirPerm)
_, err := utils.FindFileWithPrefix(fs, "/path", "prefix", "anotherprefix")
Expect(err).Should(HaveOccurred())
})
@ -773,10 +765,10 @@ var _ = Describe("Utils", Label("utils"), func() {
logger.SetLevel(v1.DebugLevel())
config.Logger = logger
err := utils.MkdirAll(fs, filepath.Join(bootDir, "grub2"), constants.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(bootDir, "grub2"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, filepath.Dir(filepath.Join(rootDir, constants.GrubConf)), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Dir(filepath.Join(rootDir, constants.GrubConf)), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, constants.GrubConf), []byte("console=tty1"), 0644)
@ -798,17 +790,17 @@ var _ = Describe("Utils", Label("utils"), func() {
})
It("installs with efi firmware", Label("efi"), func() {
err := utils.MkdirAll(fs, filepath.Join(rootDir, "/usr/share/efi/x86_64/"), constants.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(rootDir, "/usr/share/efi/x86_64/"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, "/usr/share/efi/x86_64/", constants.SignedShim), []byte(""), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, "/usr/share/efi/x86_64/grub.efi"), []byte(""), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, filepath.Join(rootDir, "/x86_64/"), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Join(rootDir, "/x86_64/"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, "/x86_64/loopback.mod"), []byte(""), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
err = utils.MkdirAll(fs, filepath.Join(rootDir, "/etc/"), constants.DirPerm)
err = fsutils.MkdirAll(fs, filepath.Join(rootDir, "/etc/"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, "/etc/os-release"), []byte("ID=\"suse\""), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
@ -839,7 +831,7 @@ var _ = Describe("Utils", Label("utils"), func() {
Expect(err.Error()).To(ContainSubstring("modules"))
})
It("fails with efi if no grub files exist", Label("efi"), func() {
err := utils.MkdirAll(fs, filepath.Join(rootDir, "/x86_64/"), constants.DirPerm)
err := fsutils.MkdirAll(fs, filepath.Join(rootDir, "/x86_64/"), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
err = fs.WriteFile(filepath.Join(rootDir, "/x86_64/loopback.mod"), []byte(""), constants.FilePerm)
Expect(err).ShouldNot(HaveOccurred())
@ -993,65 +985,6 @@ var _ = Describe("Utils", Label("utils"), func() {
Expect(mnt).To(BeFalse())
})
})
Describe("HasSquashedRecovery", Label("squashedRec"), func() {
var squashedImg string
var part *v1.Partition
BeforeEach(func() {
squashedImg = filepath.Join(constants.LiveDir, "cOS", constants.RecoverySquashFile)
part = &v1.Partition{
MountPoint: constants.LiveDir,
Path: "/some/device",
}
})
It("has squashed image from a mounted recovery", func() {
// mount recovery
err := mounter.Mount(part.Path, constants.LiveDir, "auto", []string{})
Expect(err).ShouldNot(HaveOccurred())
// create squashfs
err = utils.MkdirAll(config.Fs, filepath.Dir(squashedImg), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = config.Fs.Create(squashedImg)
Expect(err).ShouldNot(HaveOccurred())
squash, err := utils.HasSquashedRecovery(config, part)
Expect(err).ShouldNot(HaveOccurred())
Expect(squash).To(BeTrue())
})
It("does not have squashed image from a mounted recovery", func() {
// mount recovery
err := mounter.Mount(part.Path, constants.LiveDir, "auto", []string{})
Expect(err).ShouldNot(HaveOccurred())
squash, err := utils.HasSquashedRecovery(config, part)
Expect(err).ShouldNot(HaveOccurred())
Expect(squash).To(BeFalse())
})
It("has squashed image from a not mounted recovery", func() {
// squashed image on temp dir
squashedImg = filepath.Join("/tmp/elemental", "cOS", constants.RecoverySquashFile)
// create squashfs
err := utils.MkdirAll(config.Fs, filepath.Dir(squashedImg), constants.DirPerm)
Expect(err).ShouldNot(HaveOccurred())
_, err = config.Fs.Create(squashedImg)
Expect(err).ShouldNot(HaveOccurred())
squash, err := utils.HasSquashedRecovery(config, part)
Expect(err).ShouldNot(HaveOccurred())
Expect(squash).To(BeTrue())
})
It("does not have squashed image from a not mounted recovery", func() {
squash, err := utils.HasSquashedRecovery(config, part)
Expect(err).ShouldNot(HaveOccurred())
Expect(squash).To(BeFalse())
})
It("fails to mount recovery", func() {
mounter.ErrorOnMount = true
squash, err := utils.HasSquashedRecovery(config, part)
Expect(err).Should(HaveOccurred())
Expect(squash).To(BeFalse())
})
})
Describe("CleanStack", Label("CleanStack"), func() {
var cleaner *utils.CleanStack
BeforeEach(func() {