package action import ( "bytes" "os" "syscall" agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/utils" 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" ghwMock "github.com/kairos-io/kairos-sdk/ghw/mocks" sdkTypes "github.com/kairos-io/kairos-sdk/types" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" ) var _ = Describe("Bootentries tests", Label("bootentry"), func() { var config *agentConfig.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 ghwMock.GhwMock 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") var err error fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{}) // Create proper dir structure for our EFI partition contentens 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{} config = agentConfig.NewConfig( agentConfig.WithFs(fs), agentConfig.WithRunner(runner), agentConfig.WithLogger(logger), agentConfig.WithMounter(mounter), agentConfig.WithSyscall(syscallMock), agentConfig.WithClient(client), agentConfig.WithCloudInitRunner(cloudInit), agentConfig.WithImageExtractor(extractor), ) config.Config = collector.Config{} mainDisk := sdkTypes.Disk{ Name: "device", Partitions: []*sdkTypes.Partition{ { Name: "device1", FilesystemLabel: "COS_GRUB", FS: "ext4", MountPoint: "/efi", }, }, } ghwTest = ghwMock.GhwMock{} ghwTest.AddDisk(mainDisk) ghwTest.CreateDevices() }) AfterEach(func() { ghwTest.Clean() cleanup() }) Context("Under Uki", func() { BeforeEach(func() { err := fs.Mkdir("/proc", os.ModeDir|os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/proc/cmdline", []byte("rd.immucore.uki"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) }) Context("ListBootEntries", func() { It("fails to list the boot entries when there is no loader.conf", func() { err := ListBootEntries(config) Expect(err).To(HaveOccurred()) }) }) Context("ListSystemdEntries", func() { It("lists the boot entries if there is any", func() { err := fs.WriteFile("/efi/loader/loader.conf", []byte("timeout 5\ndefault kairos\nrecovery kairos2\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/active.conf", []byte("title kairos\nefi /EFI/kairos/active.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/passive.conf", []byte("title kairos (fallback)\nefi /EFI/kairos/passive.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/recovery.conf", []byte("title kairos recovery\nefi /EFI/kairos/recovery.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/statereset.conf", []byte("title kairos state reset (auto)\nefi /EFI/kairos/statereset.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) entries, err := listSystemdEntries(config, &sdkTypes.Partition{MountPoint: "/efi"}) Expect(err).ToNot(HaveOccurred()) Expect(entries).To(HaveLen(4)) Expect(entries).To(ContainElement("cos")) Expect(entries).To(ContainElement("fallback")) Expect(entries).To(ContainElement("recovery")) Expect(entries).To(ContainElement("statereset")) }) It("list empty boot entries if there is none", func() { entries, err := listSystemdEntries(config, &sdkTypes.Partition{MountPoint: "/efi"}) Expect(err).ToNot(HaveOccurred()) Expect(entries).To(HaveLen(0)) }) }) Context("SelectBootEntry", func() { It("fails to select the boot entry if it doesnt exist", func() { err := SelectBootEntry(config, "kairos") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("does not exist")) }) It("works without boot assessment", func() { err := fs.WriteFile("/efi/loader/entries/active.conf", []byte("title kairos\nefi /EFI/kairos/active.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/loader.conf", []byte("default active.conf"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = SelectBootEntry(config, "active") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to active")) reader, err := utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) }) It("selects the boot entry in a default installation", func() { err := fs.WriteFile("/efi/loader/entries/active+2-1.conf", []byte("title kairos\nefi /EFI/kairos/active.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/passive+3.conf", []byte("title kairos (fallback)\nefi /EFI/kairos/passive.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/recovery+1-2.conf", []byte("title kairos recovery\nefi /EFI/kairos/recovery.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/statereset+2-1.conf", []byte("title kairos state reset (auto)\nefi /EFI/kairos/statereset.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/loader.conf", []byte(""), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = SelectBootEntry(config, "fallback") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to fallback")) reader, err := utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("passive+3.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "recovery") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to recovery")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("recovery+1-2.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "statereset") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to statereset")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("statereset+2-1.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "cos") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to cos")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active+2-1.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) // also works using active (we want to get rid of the word cos later but this also needs to be applied in GRUB) err = SelectBootEntry(config, "active") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to active")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active+2-1.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) }) It("selects the boot entry in a extend-cmdline installation with boot branding", func() { err := fs.WriteFile("/efi/loader/entries/active_install-mode_awesomeos.conf", []byte("title awesomeos\nefi /EFI/kairos/active_install-mode_awesomeos.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/passive_install-mode_awesomeos+3.conf", []byte("title awesomeos (fallback)\nefi /EFI/kairos/passive_install-mode_awesomeos.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/recovery_install-mode_awesomeos.conf", []byte("title awesomeos recovery\nefi /EFI/kairos/recovery_install-mode_awesomeos.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/statereset_install-mode_awesomeos.conf", []byte("title awesomeos state reset (auto)\nefi /EFI/kairos/statereset_install-mode_awesomeos.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/loader.conf", []byte(""), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = SelectBootEntry(config, "fallback") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to fallback")) reader, err := utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("passive_install-mode_awesomeos+3.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "recovery") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to recovery")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("recovery_install-mode_awesomeos.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "statereset") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to statereset")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("statereset_install-mode_awesomeos.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "cos") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to cos")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active_install-mode_awesomeos.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) // also works using active (we want to get rid of the word cos later but this also needs to be applied in GRUB) err = SelectBootEntry(config, "active") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to active")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active_install-mode_awesomeos.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) }) It("selects the boot entry in a extra-cmdline installation", func() { err := fs.WriteFile("/efi/loader/entries/active.conf", []byte("title Kairos\nefi /EFI/kairos/active.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/active_foobar.conf", []byte("title Kairos\nefi /EFI/kairos/active_foobar.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/passive+3.conf", []byte("title Kairos (fallback)\nefi /EFI/kairos/passive.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/passive_foobar.conf", []byte("title Kairos (fallback)\nefi /EFI/kairos/passive_foobar.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/recovery+3.conf", []byte("title Kairos recovery\nefi /EFI/kairos/recovery.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/recovery_foobar.conf", []byte("title Kairos recovery\nefi /EFI/kairos/recovery_foobar.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/statereset.conf", []byte("title Kairos state reset (auto)\nefi /EFI/kairos/statereset.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/entries/statereset_foobar.conf", []byte("title Kairos state reset (auto)\nefi /EFI/kairos/state_reset_foobar.efi\n"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/efi/loader/loader.conf", []byte("default active.conf"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = SelectBootEntry(config, "fallback") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to fallback")) reader, err := utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("passive+3.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "fallback foobar") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to fallback foobar")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("passive_foobar.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "recovery") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to recovery")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("recovery+3.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "recovery foobar") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to recovery foobar")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("recovery_foobar.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "statereset") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to statereset")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("statereset.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "statereset foobar") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to statereset foobar")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("statereset_foobar.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "cos") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to cos")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "cos foobar") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to cos foobar")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active_foobar.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) // also works using active (we want to get rid of the word cos later but this also needs to be applied in GRUB) err = SelectBootEntry(config, "active") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to active")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) err = SelectBootEntry(config, "active foobar") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to active foobar")) reader, err = utils.SystemdBootConfReader(fs, "/efi/loader/loader.conf") Expect(err).ToNot(HaveOccurred()) Expect(reader["default"]).To(Equal("active_foobar.conf")) // Should have called a remount to make it RW Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT, "")).To(BeTrue()) // Should have called a remount to make it RO Expect(syscallMock.WasMountCalledWith( "", "/efi", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")).To(BeTrue()) }) }) }) Context("Under grub", func() { Context("ListBootEntries", func() { It("fails to list the boot entries when there is no grub files", func() { err := ListBootEntries(config) Expect(err).To(HaveOccurred()) }) }) Context("ListSystemdEntries", func() { It("lists the boot entries if there is any", func() { err := fs.WriteFile("/etc/cos/grub.cfg", []byte("whatever whatever --id kairos {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/run/initramfs/cos-state/grub/grub.cfg", []byte("whatever whatever --id kairos2 {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/etc/kairos/branding/grubmenu.cfg", []byte("whatever whatever --id kairos3 {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) entries, err := listGrubEntries(config) Expect(err).ToNot(HaveOccurred()) Expect(entries).To(HaveLen(3)) Expect(entries).To(ContainElement("kairos")) Expect(entries).To(ContainElement("kairos2")) Expect(entries).To(ContainElement("kairos3")) }) It("list empty boot entries if there is none", func() { entries, err := listGrubEntries(config) Expect(err).ToNot(HaveOccurred()) Expect(entries).To(HaveLen(0)) }) }) Context("SelectBootEntry", func() { BeforeEach(func() { runner.SideEffect = func(cmd string, args ...string) ([]byte, error) { switch cmd { default: return []byte{}, nil } } }) It("fails to select the boot entry if it doesnt exist", func() { err := SelectBootEntry(config, "kairos") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("does not exist")) }) It("selects the boot entry", func() { err := fs.WriteFile("/etc/cos/grub.cfg", []byte("whatever whatever --id kairos {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/run/initramfs/cos-state/grub/grub.cfg", []byte("whatever whatever --id kairos2 {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) err = fs.WriteFile("/etc/kairos/branding/grubmenu.cfg", []byte("whatever whatever --id kairos3 {"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) Expect(fs.Mkdir("/oem", os.ModePerm)).To(Succeed()) err = SelectBootEntry(config, "kairos") Expect(err).ToNot(HaveOccurred()) Expect(memLog.String()).To(ContainSubstring("Default boot entry set to kairos")) variables, err := utils.ReadPersistentVariables("/oem/grubenv", config) Expect(err).ToNot(HaveOccurred()) Expect(variables["next_entry"]).To(Equal("kairos")) }) }) }) })