kairos-agent/pkg/action/reset_test.go

239 lines
7.6 KiB
Go
Raw Normal View History

/*
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 action_test
import (
"bytes"
"errors"
"fmt"
2023-07-10 12:39:48 +00:00
"github.com/kairos-io/kairos-agent/v2/pkg/action"
2024-09-17 13:27:31 +00:00
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
2023-07-10 12:39:48 +00:00
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
2024-09-17 13:27:31 +00:00
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
2023-07-10 12:39:48 +00:00
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
2024-09-17 13:27:31 +00:00
ghwMock "github.com/kairos-io/kairos-sdk/ghw/mocks"
sdkTypes "github.com/kairos-io/kairos-sdk/types"
"path/filepath"
2024-09-17 13:27:31 +00:00
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/twpayne/go-vfs/v5"
"github.com/twpayne/go-vfs/v5/vfst"
)
var _ = Describe("Reset action tests", func() {
var config *agentConfig.Config
var runner *v1mock.FakeRunner
var fs vfs.FS
2024-03-01 11:27:26 +00:00
var logger sdkTypes.KairosLogger
var mounter *v1mock.ErrorMounter
var syscall *v1mock.FakeSyscall
var client *v1mock.FakeHTTPClient
var cloudInit *v1mock.FakeCloudInitRunner
var cleanup func()
var memLog *bytes.Buffer
2024-09-17 13:27:31 +00:00
var ghwTest ghwMock.GhwMock
var extractor *v1mock.FakeImageExtractor
BeforeEach(func() {
runner = v1mock.NewFakeRunner()
syscall = &v1mock.FakeSyscall{}
mounter = v1mock.NewErrorMounter()
client = &v1mock.FakeHTTPClient{}
memLog = &bytes.Buffer{}
2024-03-01 11:27:26 +00:00
logger = sdkTypes.NewBufferLogger(memLog)
extractor = v1mock.NewFakeImageExtractor(logger)
var err error
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{
"/dev/loop-control": "",
"/dev/loop0": "",
})
2023-06-23 12:49:38 +00:00
Expect(err).Should(BeNil())
cloudInit = &v1mock.FakeCloudInitRunner{}
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),
)
})
AfterEach(func() { cleanup() })
Describe("Reset Action", Label("reset"), func() {
var spec *v1.ResetSpec
var reset *action.ResetAction
var cmdFail, bootedFrom string
var err error
BeforeEach(func() {
2023-06-23 12:49:38 +00:00
Expect(err).ShouldNot(HaveOccurred())
cmdFail = ""
recoveryImg := filepath.Join(constants.RunningStateDir, "cOS", constants.RecoveryImgFile)
err = fsutils.MkdirAll(fs, filepath.Dir(recoveryImg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(recoveryImg)
Expect(err).To(BeNil())
2024-09-17 13:27:31 +00:00
mainDisk := sdkTypes.Disk{
2023-06-23 12:49:38 +00:00
Name: "device",
2024-09-17 13:27:31 +00:00
Partitions: []*sdkTypes.Partition{
2023-06-23 12:49:38 +00:00
{
Name: "device1",
FilesystemLabel: "COS_GRUB",
2024-09-17 13:27:31 +00:00
FS: "ext4",
2023-06-23 12:49:38 +00:00
},
{
Name: "device2",
2024-09-27 14:10:49 +00:00
FilesystemLabel: "COS_OEM",
2024-09-17 13:27:31 +00:00
FS: "ext4",
2024-09-27 14:10:49 +00:00
MountPoint: "/oem",
2023-06-23 12:49:38 +00:00
},
{
Name: "device3",
2024-09-27 14:10:49 +00:00
FilesystemLabel: "COS_RECOVERY",
2024-09-17 13:27:31 +00:00
FS: "ext4",
2024-09-27 14:10:49 +00:00
MountPoint: "/run/initramfs/cos-state",
2023-06-23 12:49:38 +00:00
},
{
Name: "device4",
2024-09-27 14:10:49 +00:00
FilesystemLabel: "COS_STATE",
2024-09-17 13:27:31 +00:00
FS: "ext4",
2023-06-23 12:49:38 +00:00
},
{
Name: "device5",
2024-09-27 14:10:49 +00:00
FilesystemLabel: "COS_PERSISTENT",
2024-09-17 13:27:31 +00:00
FS: "ext4",
2023-06-23 12:49:38 +00:00
},
},
}
2024-09-17 13:27:31 +00:00
ghwTest = ghwMock.GhwMock{}
2023-06-23 12:49:38 +00:00
ghwTest.AddDisk(mainDisk)
ghwTest.CreateDevices()
Expect(fsutils.MkdirAll(fs, constants.EfiDevice, constants.DirPerm)).ToNot(HaveOccurred())
bootedFrom = constants.SystemLabel
runner.SideEffect = func(cmd string, args ...string) ([]byte, error) {
if cmd == cmdFail {
return []byte{}, errors.New("Command failed")
}
switch cmd {
case "cat":
return []byte(bootedFrom), nil
2023-06-23 12:49:38 +00:00
default:
return []byte{}, nil
}
}
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 = fsutils.MkdirAll(fs, filepath.Dir(grubCfg), constants.DirPerm)
Expect(err).To(BeNil())
_, err = fs.Create(grubCfg)
Expect(err).To(BeNil())
// create the fake grub dir with modules, it needs an arch in the path
Expect(fsutils.MkdirAll(fs, filepath.Join(spec.Active.MountPoint, constants.Archx86), constants.DirPerm)).ToNot(HaveOccurred())
for _, mod := range constants.GetGrubModules() {
_, err = fs.Create(filepath.Join(spec.Active.MountPoint, constants.Archx86, mod))
Expect(err).ToNot(HaveOccurred())
}
// create fake shim
Expect(fsutils.MkdirAll(fs, filepath.Join(spec.Active.MountPoint, "/usr/share/efi/x86_64/"), constants.DirPerm)).ToNot(HaveOccurred())
_, err = fs.Create(filepath.Join(spec.Active.MountPoint, "/usr/share/efi/x86_64/", "shim.efi"))
Expect(err).ToNot(HaveOccurred())
// create fake grub
Expect(fsutils.MkdirAll(fs, filepath.Join(spec.Active.MountPoint, "/usr/share/efi/x86_64/"), constants.DirPerm)).ToNot(HaveOccurred())
_, err = fs.Create(filepath.Join(spec.Active.MountPoint, "/usr/share/efi/x86_64/", "grub.efi"))
Expect(err).ToNot(HaveOccurred())
reset = action.NewResetAction(config, spec)
})
2023-06-23 12:49:38 +00:00
AfterEach(func() {
ghwTest.Clean()
})
Describe("With EFI", func() {
It("Successfully resets on non-squashfs recovery", func() {
Expect(reset.Run()).To(BeNil())
})
It("Successfully resets on non-squashfs recovery including persistent data", func() {
spec.FormatPersistent = true
spec.FormatOEM = true
Expect(reset.Run()).To(BeNil())
})
It("Successfully resets from a squashfs recovery image", Label("channel"), func() {
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())
})
It("Successfully resets despite having errors on hooks", func() {
cloudInit.Error = true
Expect(reset.Run()).To(BeNil())
})
It("Successfully resets from a docker image", Label("docker"), func() {
spec.Active.Source = v1.NewDockerSrc("my/image:latest")
Expect(reset.Run()).To(BeNil())
})
It("Fails formatting state partition", func() {
cmdFail = "mkfs.ext4"
Expect(reset.Run()).NotTo(BeNil())
Expect(runner.IncludesCmds([][]string{{"mkfs.ext4"}}))
})
It("Fails setting the active label on non-squashfs recovery", func() {
cmdFail = "tune2fs"
Expect(reset.Run()).NotTo(BeNil())
})
It("Fails setting the passive label on squashfs recovery", func() {
cmdFail = "tune2fs"
Expect(reset.Run()).NotTo(BeNil())
Expect(runner.IncludesCmds([][]string{{"tune2fs"}}))
})
It("Fails mounting partitions", func() {
mounter.ErrorOnMount = true
Expect(reset.Run()).NotTo(BeNil())
})
It("Fails unmounting partitions", func() {
mounter.ErrorOnUnmount = true
Expect(reset.Run()).NotTo(BeNil())
})
It("Fails unpacking docker image ", func() {
spec.Active.Source = v1.NewDockerSrc("my/image:latest")
extractor.SideEffect = func(imageRef, destination, platformRef string) error {
return fmt.Errorf("error")
}
Expect(reset.Run()).NotTo(BeNil())
})
})
})
})