mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-06-03 01:44:53 +00:00
Copy any found sysextensions into active+passive efi dir (#372)
This commit is contained in:
parent
c1d1a33114
commit
b176b47f56
@ -33,6 +33,7 @@ var FirstBoot = []Interface{
|
||||
|
||||
// AfterUkiInstall sets which Hooks to run after uki runs the install action
|
||||
var AfterUkiInstall = []Interface{
|
||||
&SysExtPostInstall{},
|
||||
&Lifecycle{},
|
||||
}
|
||||
|
||||
|
187
internal/agent/hooks/hooks_test.go
Normal file
187
internal/agent/hooks/hooks_test.go
Normal file
@ -0,0 +1,187 @@
|
||||
package hook_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/jaypipes/ghw/pkg/block"
|
||||
_ "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks"
|
||||
hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/twpayne/go-vfs/v4"
|
||||
"github.com/twpayne/go-vfs/v4/vfst"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Hooks Suite")
|
||||
}
|
||||
|
||||
var _ = Describe("Hooks", func() {
|
||||
var cfg *config.Config
|
||||
var fs vfs.FS
|
||||
var logger sdkTypes.KairosLogger
|
||||
var runner *v1mock.FakeRunner
|
||||
var mounter *v1mock.ErrorMounter
|
||||
var syscallMock *v1mock.FakeSyscall
|
||||
var client *v1mock.FakeHTTPClient
|
||||
var cloudInit *v1mock.FakeCloudInitRunner
|
||||
var cleanup func()
|
||||
var memLog *bytes.Buffer
|
||||
var extractor *v1mock.FakeImageExtractor
|
||||
var ghwTest v1mock.GhwMock
|
||||
var err error
|
||||
|
||||
Context("SysExtPostInstall", func() {
|
||||
BeforeEach(func() {
|
||||
runner = v1mock.NewFakeRunner()
|
||||
syscallMock = &v1mock.FakeSyscall{}
|
||||
mounter = v1mock.NewErrorMounter()
|
||||
client = &v1mock.FakeHTTPClient{}
|
||||
memLog = &bytes.Buffer{}
|
||||
logger = sdkTypes.NewBufferLogger(memLog)
|
||||
extractor = v1mock.NewFakeImageExtractor(logger)
|
||||
logger.SetLevel("debug")
|
||||
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{})
|
||||
// Create proper dir structure for our EFI partition contents
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/efi/loader/entries", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/efi/EFI/BOOT", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/efi/EFI/kairos", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/etc/cos/", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/run/initramfs/cos-state/grub/", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fsutils.MkdirAll(fs, "/etc/kairos/branding/", os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
cloudInit = &v1mock.FakeCloudInitRunner{}
|
||||
cfg = config.NewConfig(
|
||||
config.WithFs(fs),
|
||||
config.WithRunner(runner),
|
||||
config.WithLogger(logger),
|
||||
config.WithMounter(mounter),
|
||||
config.WithSyscall(syscallMock),
|
||||
config.WithClient(client),
|
||||
config.WithCloudInitRunner(cloudInit),
|
||||
config.WithImageExtractor(extractor),
|
||||
)
|
||||
cfg.Config = collector.Config{}
|
||||
|
||||
mainDisk := block.Disk{
|
||||
Name: "device",
|
||||
Partitions: []*block.Partition{
|
||||
{
|
||||
Name: "device1",
|
||||
FilesystemLabel: "COS_GRUB",
|
||||
Type: "ext4",
|
||||
MountPoint: "/efi",
|
||||
},
|
||||
},
|
||||
}
|
||||
ghwTest = v1mock.GhwMock{}
|
||||
ghwTest.AddDisk(mainDisk)
|
||||
ghwTest.CreateDevices()
|
||||
})
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
It("should copy all files with .sysext.raw extension", func() {
|
||||
err = fsutils.MkdirAll(fs, cnst.LiveDir, os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "test1.sysext.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "test2.sysext.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
// we expect them to be here as its where we mount the efi partition but then we fake unmount
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "test1.sysext.raw"))
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "test2.sysext.raw"))
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("should ignore files without .sysext.raw extension", func() {
|
||||
err = fsutils.MkdirAll(fs, cnst.LiveDir, os.ModeDir|os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "test1.sysext.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "test2.sysext.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "hello.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "hello.sysext.what.raw"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
err = fs.WriteFile(filepath.Join(cnst.LiveDir, "hello.sysext"), []byte("test"), os.ModePerm)
|
||||
Expect(err).Should(BeNil())
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
// we expect them to be here as its where we mount the efi partition but then we fake unmount
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "test1.sysext.raw"))
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "test2.sysext.raw"))
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "hello.raw"))
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "hello.sysext.what.raw"))
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
_, err = fs.Stat(filepath.Join(cnst.EfiDir, "EFI/kairos/active.efi.extra.d/", "hello.sysext"))
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
It("doesn't error if it cant find the efi partition", func() {
|
||||
ghwTest.Clean()
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("errors if it cant mount the efi partition and strict is set", func() {
|
||||
ghwTest.Clean()
|
||||
cfg.FailOnBundleErrors = true
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
It("doesn't error if it cant mount the efi partition", func() {
|
||||
mounter.ErrorOnMount = true
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("errors if it cant mount the efi partition and strict is set", func() {
|
||||
mounter.ErrorOnMount = true
|
||||
cfg.FailOnBundleErrors = true
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
It("doesn't error if it cant create the dirs", func() {
|
||||
ROfs := vfs.NewReadOnlyFS(fs)
|
||||
cfg.Fs = ROfs
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("errors if it cant create the dirs and strict is set", func() {
|
||||
cfg.FailOnBundleErrors = true
|
||||
ROfs := vfs.NewReadOnlyFS(fs)
|
||||
cfg.Fs = ROfs
|
||||
postInstall := hook.SysExtPostInstall{}
|
||||
err = postInstall.Run(*cfg, nil)
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
})
|
||||
})
|
88
internal/agent/hooks/sysext.go
Normal file
88
internal/agent/hooks/sysext.go
Normal file
@ -0,0 +1,88 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"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/types/v1"
|
||||
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/utils/partitions"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SysExtPostInstall struct{}
|
||||
|
||||
func (b SysExtPostInstall) Run(c config.Config, _ v1.Spec) error {
|
||||
c.Logger.Logger.Debug().Msg("Running SysExtPostInstall hook")
|
||||
// mount efi partition
|
||||
efiPart, err := partitions.GetEfiPartition()
|
||||
if err != nil {
|
||||
c.Logger.Errorf("failed to get EFI partition: %s", err)
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
mounted, _ := c.Mounter.IsMountPoint(constants.EfiDir)
|
||||
|
||||
if !mounted {
|
||||
err = c.Mounter.Mount(efiPart.Path, constants.EfiDir, efiPart.FS, []string{"rw"})
|
||||
if err != nil {
|
||||
c.Logger.Errorf("failed to mount EFI partition: %s", err)
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
defer func() {
|
||||
_ = c.Mounter.Unmount(constants.EfiDir)
|
||||
}()
|
||||
} else {
|
||||
// If its mounted, try to remount it RW
|
||||
err = c.Mounter.Mount(efiPart.Path, constants.EfiDir, efiPart.FS, []string{"remount,rw"})
|
||||
defer func() {
|
||||
_ = c.Mounter.Unmount(constants.EfiDir)
|
||||
}()
|
||||
}
|
||||
|
||||
activeDir := filepath.Join(constants.EfiDir, "EFI/kairos/active.efi.extra.d/")
|
||||
passiveDir := filepath.Join(constants.EfiDir, "EFI/kairos/passive.efi.extra.d/")
|
||||
for _, dir := range []string{activeDir, passiveDir} {
|
||||
err = fsutils.MkdirAll(c.Fs, dir, 0755)
|
||||
if err != nil {
|
||||
c.Logger.Errorf("failed to create directory %s: %s", dir, err)
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = fsutils.WalkDirFs(c.Fs, constants.LiveDir, func(path string, info fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(info.Name(), ".sysext.raw") {
|
||||
// copy it to /EFI/Kairos/{active,passive}.efi.extra.d/
|
||||
err = fsutils.Copy(c.Fs, path, filepath.Join(activeDir, info.Name()))
|
||||
if err != nil {
|
||||
c.Logger.Errorf("failed to copy %s to %s: %s", path, activeDir, err)
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
c.Logger.Debugf("copied %s to %s", path, activeDir)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if c.FailOnBundleErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Done SysExtPostInstall hook")
|
||||
return nil
|
||||
}
|
@ -21,6 +21,7 @@ package fsutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -243,3 +244,32 @@ func readDir(fs v1.FS, dirname string) ([]fs.DirEntry, error) {
|
||||
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
|
||||
return dirs, nil
|
||||
}
|
||||
|
||||
// Copy copies src to dst like the cp command.
|
||||
func Copy(fs v1.FS, src, dst string) error {
|
||||
if dst == src {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
srcF, err := fs.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcF.Close()
|
||||
|
||||
info, err := srcF.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dstF, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstF.Close()
|
||||
|
||||
if _, err := io.Copy(dstF, srcF); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user