From 7059083fcfda89c5d4622c5c14549d4510ab0fe4 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Mon, 29 Jul 2024 14:53:28 +0300 Subject: [PATCH 1/7] Extract installEntry to a method so that is can be reused to upgrade arbitrary entries Signed-off-by: Dimitris Karakasilis --- pkg/uki/upgrade.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pkg/uki/upgrade.go b/pkg/uki/upgrade.go index e5e7f9e3..a88660e9 100644 --- a/pkg/uki/upgrade.go +++ b/pkg/uki/upgrade.go @@ -122,9 +122,7 @@ func (i *UpgradeAction) Run() (err error) { return nil } -// installRecovery replaces the "recovery" role efi and conf files with -// the UnassignedArtifactRole efi and loader files from dir -func (i *UpgradeAction) installRecovery() error { +func (i *UpgradeAction) installEntry(entry string) error { tmpDir, err := os.MkdirTemp("", "") if err != nil { i.cfg.Logger.Errorf("creating a tmp dir: %s", err.Error()) @@ -142,13 +140,13 @@ func (i *UpgradeAction) installRecovery() error { err = copyFile( filepath.Join(tmpDir, "EFI", "kairos", UnassignedArtifactRole+".efi"), - filepath.Join(constants.UkiEfiDir, "EFI", "kairos", "recovery.efi")) + filepath.Join(constants.UkiEfiDir, "EFI", "kairos", fmt.Sprintf("%s.efi", entry))) if err != nil { i.cfg.Logger.Errorf("copying efi files: %s", err.Error()) return err } - targetConfPath := filepath.Join(constants.UkiEfiDir, "loader", "entries", "recovery.conf") + targetConfPath := filepath.Join(constants.UkiEfiDir, "loader", "entries", fmt.Sprintf("%s.conf", entry)) err = copyFile( filepath.Join(tmpDir, "loader", "entries", UnassignedArtifactRole+".conf"), targetConfPath) @@ -156,13 +154,24 @@ func (i *UpgradeAction) installRecovery() error { i.cfg.Logger.Errorf("copying conf files: %s", err.Error()) return err } - err = replaceRoleInKey(targetConfPath, "efi", UnassignedArtifactRole, "recovery", i.cfg.Logger) + err = replaceRoleInKey(targetConfPath, "efi", UnassignedArtifactRole, entry, i.cfg.Logger) if err != nil { i.cfg.Logger.Errorf("replacing role in in key %s: %s", "efi", err.Error()) return err } - err = replaceConfTitle(targetConfPath, "recovery") + return nil +} + +// installRecovery replaces the "recovery" role efi and conf files with +// the UnassignedArtifactRole efi and loader files from dir +func (i *UpgradeAction) installRecovery() error { + if err := i.installEntry("recovery"); err != nil { + return err + } + + targetConfPath := filepath.Join(constants.UkiEfiDir, "loader", "entries", "recovery.conf") + err := replaceConfTitle(targetConfPath, "recovery") if err != nil { i.cfg.Logger.Errorf("replacing conf title: %s", err.Error()) return err From c589ae1ce333fd5a44054c8112ffc83c796e1601 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Mon, 29 Jul 2024 16:51:14 +0300 Subject: [PATCH 2/7] [WIP] Properly handle single entries in bootentry command Signed-off-by: Dimitris Karakasilis --- internal/agent/upgrade.go | 23 ++++++++++++++--------- main.go | 3 +++ pkg/action/bootentries.go | 9 ++++++--- pkg/types/v1/config.go | 11 ++++++----- pkg/uki/upgrade.go | 4 ++++ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/internal/agent/upgrade.go b/internal/agent/upgrade.go index 774692e5..7ac41580 100644 --- a/internal/agent/upgrade.go +++ b/internal/agent/upgrade.go @@ -65,18 +65,18 @@ func ListNewerReleases(includePrereleases bool) ([]string, error) { } func Upgrade( - source string, force, strictValidations bool, dirs []string, preReleases, upgradeRecovery bool) error { + source string, force, strictValidations bool, dirs []string, singleEntry string, preReleases, upgradeRecovery bool) error { bus.Manager.Initialize() if internalutils.UkiBootMode() == internalutils.UkiHDD { - return upgradeUki(source, dirs, strictValidations, upgradeRecovery) + return upgradeUki(source, dirs, singleEntry, strictValidations, upgradeRecovery) } else { return upgrade(source, force, strictValidations, dirs, preReleases, upgradeRecovery) } } func upgrade(source string, force, strictValidations bool, dirs []string, preReleases, upgradeRecovery bool) error { - upgradeSpec, c, err := generateUpgradeSpec(source, force, strictValidations, dirs, preReleases, upgradeRecovery) + upgradeSpec, c, err := generateUpgradeSpec(source, force, strictValidations, dirs, "", preReleases, upgradeRecovery) if err != nil { return err } @@ -135,13 +135,17 @@ func newerReleases() (versioneer.TagList, error) { // generateUpgradeConfForCLIArgs creates a kairos configuration for `--source` and `--recovery` // command line arguments. It will be added to the rest of the configurations. -func generateUpgradeConfForCLIArgs(source string, upgradeRecovery bool) (string, error) { +func generateUpgradeConfForCLIArgs(source, singleEntry string, upgradeRecovery bool) (string, error) { upgradeConfig := ExtraConfigUpgrade{} if upgradeRecovery { upgradeConfig.Upgrade.Recovery = true } + // if upgradeSingleEntry { + // upgradeConfig.Upgrade.Recovery = true + // } + // Set uri both for active and recovery because we don't know what we are // actually upgrading. The "upgradeRecovery" is just the command line argument. // The user might have set it to "true" in the kairos config. Since we don't @@ -157,8 +161,8 @@ func generateUpgradeConfForCLIArgs(source string, upgradeRecovery bool) (string, return string(d), err } -func generateUpgradeSpec(sourceImageURL string, force, strictValidations bool, dirs []string, preReleases, upgradeRecovery bool) (*v1.UpgradeSpec, *config.Config, error) { - cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, upgradeRecovery) +func generateUpgradeSpec(sourceImageURL string, force, strictValidations bool, dirs []string, singleEntry string, preReleases, upgradeRecovery bool) (*v1.UpgradeSpec, *config.Config, error) { + cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, singleEntry, upgradeRecovery) if err != nil { return nil, nil, err } @@ -201,8 +205,8 @@ func getReleasesFromProvider(includePrereleases bool) ([]string, error) { return result, nil } -func upgradeUki(source string, dirs []string, strictValidations, upgradeRecovery bool) error { - cliConf, err := generateUpgradeConfForCLIArgs(source, upgradeRecovery) +func upgradeUki(source string, dirs []string, singleEntry string, strictValidations, upgradeRecovery bool) error { + cliConf, err := generateUpgradeConfForCLIArgs(source, singleEntry, upgradeRecovery) if err != nil { return err } @@ -240,7 +244,8 @@ func upgradeUki(source string, dirs []string, strictValidations, upgradeRecovery // ExtraConfigUpgrade is the struct that holds the upgrade options that come from flags and events type ExtraConfigUpgrade struct { Upgrade struct { - Recovery bool `json:"recovery,omitempty"` + Recovery bool `json:"recovery,omitempty"` + SingleEntry string `json:"single-entry,omitempty"` RecoverySystem struct { URI string `json:"uri,omitempty"` } `json:"recovery-system,omitempty"` diff --git a/main.go b/main.go index a25068a5..374086f2 100644 --- a/main.go +++ b/main.go @@ -185,8 +185,11 @@ See https://kairos.io/docs/upgrade/manual/ for documentation. source = fmt.Sprintf("oci:%s", image) } + // TODO: Too many flags? Merge --recovery and --single-entry somehow? + // Does the new flag make sense in non-uki? (No) return agent.Upgrade(source, c.Bool("force"), c.Bool("strict-validation"), constants.GetConfigScanDirs(), + c.String("single-entry"), c.Bool("pre"), c.Bool("recovery"), ) }, diff --git a/pkg/action/bootentries.go b/pkg/action/bootentries.go index 821da599..b3367831 100644 --- a/pkg/action/bootentries.go +++ b/pkg/action/bootentries.go @@ -214,7 +214,7 @@ func systemdConfToBootName(conf string) (string, error) { return bootName, nil } - return "", fmt.Errorf("unknown systemd-boot conf: %s", conf) + return strings.ReplaceAll(fileName, "_", " "), nil } func bootNameToSystemdConf(name string) (string, error) { @@ -253,10 +253,9 @@ func bootNameToSystemdConf(name string) (string, error) { differenciator = "_" + strings.TrimPrefix(name, "statereset ") } return "statereset" + differenciator + ".conf", nil - } - return "", fmt.Errorf("unknown boot name: %s", name) + return strings.ReplaceAll(name, " ", "_") + ".conf", nil } // listBootEntriesSystemd lists the boot entries available in the systemd-boot config files @@ -320,6 +319,10 @@ func listBootEntriesSystemd(cfg *config.Config) error { func listSystemdEntries(cfg *config.Config, efiPartition *v1.Partition) ([]string, error) { var entries []string err := fsutils.WalkDirFs(cfg.Fs, filepath.Join(efiPartition.MountPoint, "loader/entries/"), func(path string, info os.DirEntry, err error) error { + if err != nil { + cfg.Logger.Errorf("Walking the dir %s", err.Error()) + } + cfg.Logger.Debugf("Checking file %s", path) if info == nil { return nil diff --git a/pkg/types/v1/config.go b/pkg/types/v1/config.go index c4fbe73a..49e32606 100644 --- a/pkg/types/v1/config.go +++ b/pkg/types/v1/config.go @@ -528,11 +528,12 @@ func (i *InstallUkiSpec) GetPartitions() ElementalPartitions { return i.Partitio func (i *InstallUkiSpec) GetExtraPartitions() PartitionList { return i.ExtraPartitions } type UpgradeUkiSpec struct { - RecoveryUpgrade bool `yaml:"recovery,omitempty" mapstructure:"recovery"` - Active Image `yaml:"system,omitempty" mapstructure:"system"` - Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` - PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` - EfiPartition *Partition `yaml:"efi-partition,omitempty" mapstructure:"efi-partition"` + RecoveryUpgrade bool `yaml:"recovery,omitempty" mapstructure:"recovery"` + UpgradeSingleEntry string `yaml:"upgrade-single-entry,omitempty" mapstructure:"upgrade-single-entry"` + Active Image `yaml:"system,omitempty" mapstructure:"system"` + Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` + PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` + EfiPartition *Partition `yaml:"efi-partition,omitempty" mapstructure:"efi-partition"` } func (i *UpgradeUkiSpec) Sanitize() error { diff --git a/pkg/uki/upgrade.go b/pkg/uki/upgrade.go index a88660e9..90642838 100644 --- a/pkg/uki/upgrade.go +++ b/pkg/uki/upgrade.go @@ -58,6 +58,10 @@ func (i *UpgradeAction) Run() (err error) { return i.installRecovery() } + if i.spec.UpgradeSingleEntry != "" { + return i.installEntry(i.spec.UpgradeSingleEntry) + } + // Dump artifact to efi dir _, err = e.DumpSource(constants.UkiEfiDir, i.spec.Active.Source) if err != nil { From 7e82580539ceb1492de123b5ad1e2339baea9e97 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Thu, 1 Aug 2024 13:04:42 +0300 Subject: [PATCH 3/7] Add flag Signed-off-by: Dimitris Karakasilis --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 374086f2..4b650f2c 100644 --- a/main.go +++ b/main.go @@ -69,6 +69,7 @@ var cmds = []*cli.Command{ Usage: "[DEPRECATED] Specify a full image reference, e.g.: quay.io/some/image:tag", }, &sourceFlag, + &cli.StringFlag{Name: "single-entry", Usage: "Specify a \"single\" systemd-boot entry to upgrade (other than active/passive/recovery)"}, &cli.BoolFlag{Name: "pre", Usage: "Include pre-releases (rc, beta, alpha)"}, &cli.BoolFlag{Name: "recovery", Usage: "Upgrade recovery"}, }, From 257d0a1c3812c4164226bfe9695c1a8d950e2aa9 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Tue, 13 Aug 2024 12:12:20 +0300 Subject: [PATCH 4/7] Unify --recover and --boot-entry upgrade options in code Signed-off-by: Dimitris Karakasilis --- internal/agent/upgrade.go | 31 ++++++++++++------------------- main.go | 18 +++++++++++++----- pkg/action/upgrade.go | 12 ++++++------ pkg/config/spec.go | 2 +- pkg/constants/constants.go | 1 + pkg/types/v1/config.go | 23 +++++++++++++++-------- pkg/uki/upgrade.go | 20 +++++++++++++------- 7 files changed, 61 insertions(+), 46 deletions(-) diff --git a/internal/agent/upgrade.go b/internal/agent/upgrade.go index 7ac41580..c9ef518e 100644 --- a/internal/agent/upgrade.go +++ b/internal/agent/upgrade.go @@ -65,18 +65,18 @@ func ListNewerReleases(includePrereleases bool) ([]string, error) { } func Upgrade( - source string, force, strictValidations bool, dirs []string, singleEntry string, preReleases, upgradeRecovery bool) error { + source string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) error { bus.Manager.Initialize() if internalutils.UkiBootMode() == internalutils.UkiHDD { - return upgradeUki(source, dirs, singleEntry, strictValidations, upgradeRecovery) + return upgradeUki(source, dirs, upgradeEntry, strictValidations) } else { - return upgrade(source, force, strictValidations, dirs, preReleases, upgradeRecovery) + return upgrade(source, force, strictValidations, dirs, upgradeEntry, preReleases) } } -func upgrade(source string, force, strictValidations bool, dirs []string, preReleases, upgradeRecovery bool) error { - upgradeSpec, c, err := generateUpgradeSpec(source, force, strictValidations, dirs, "", preReleases, upgradeRecovery) +func upgrade(source string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) error { + upgradeSpec, c, err := generateUpgradeSpec(source, force, strictValidations, dirs, upgradeEntry, preReleases) if err != nil { return err } @@ -135,16 +135,10 @@ func newerReleases() (versioneer.TagList, error) { // generateUpgradeConfForCLIArgs creates a kairos configuration for `--source` and `--recovery` // command line arguments. It will be added to the rest of the configurations. -func generateUpgradeConfForCLIArgs(source, singleEntry string, upgradeRecovery bool) (string, error) { +func generateUpgradeConfForCLIArgs(source, upgradeEntry string) (string, error) { upgradeConfig := ExtraConfigUpgrade{} - if upgradeRecovery { - upgradeConfig.Upgrade.Recovery = true - } - - // if upgradeSingleEntry { - // upgradeConfig.Upgrade.Recovery = true - // } + upgradeConfig.Upgrade.Entry = upgradeEntry // Set uri both for active and recovery because we don't know what we are // actually upgrading. The "upgradeRecovery" is just the command line argument. @@ -161,8 +155,8 @@ func generateUpgradeConfForCLIArgs(source, singleEntry string, upgradeRecovery b return string(d), err } -func generateUpgradeSpec(sourceImageURL string, force, strictValidations bool, dirs []string, singleEntry string, preReleases, upgradeRecovery bool) (*v1.UpgradeSpec, *config.Config, error) { - cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, singleEntry, upgradeRecovery) +func generateUpgradeSpec(sourceImageURL string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) (*v1.UpgradeSpec, *config.Config, error) { + cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, upgradeEntry) if err != nil { return nil, nil, err } @@ -205,8 +199,8 @@ func getReleasesFromProvider(includePrereleases bool) ([]string, error) { return result, nil } -func upgradeUki(source string, dirs []string, singleEntry string, strictValidations, upgradeRecovery bool) error { - cliConf, err := generateUpgradeConfForCLIArgs(source, singleEntry, upgradeRecovery) +func upgradeUki(source string, dirs []string, upgradeEntry string, strictValidations bool) error { + cliConf, err := generateUpgradeConfForCLIArgs(source, upgradeEntry) if err != nil { return err } @@ -244,8 +238,7 @@ func upgradeUki(source string, dirs []string, singleEntry string, strictValidati // ExtraConfigUpgrade is the struct that holds the upgrade options that come from flags and events type ExtraConfigUpgrade struct { Upgrade struct { - Recovery bool `json:"recovery,omitempty"` - SingleEntry string `json:"single-entry,omitempty"` + Entry string `json:"entry,omitempty"` RecoverySystem struct { URI string `json:"uri,omitempty"` } `json:"recovery-system,omitempty"` diff --git a/main.go b/main.go index 4b650f2c..2716b28b 100644 --- a/main.go +++ b/main.go @@ -69,7 +69,7 @@ var cmds = []*cli.Command{ Usage: "[DEPRECATED] Specify a full image reference, e.g.: quay.io/some/image:tag", }, &sourceFlag, - &cli.StringFlag{Name: "single-entry", Usage: "Specify a \"single\" systemd-boot entry to upgrade (other than active/passive/recovery)"}, + &cli.StringFlag{Name: "boot-entry", Usage: "Specify a systemd-boot entry to upgrade (other than active/passive/recovery). The value should match the name of the '.efi' file."}, &cli.BoolFlag{Name: "pre", Usage: "Include pre-releases (rc, beta, alpha)"}, &cli.BoolFlag{Name: "recovery", Usage: "Upgrade recovery"}, }, @@ -186,12 +186,20 @@ See https://kairos.io/docs/upgrade/manual/ for documentation. source = fmt.Sprintf("oci:%s", image) } - // TODO: Too many flags? Merge --recovery and --single-entry somehow? - // Does the new flag make sense in non-uki? (No) + if c.Bool("recovery") && c.String("boot-entry") != "" { + return fmt.Errorf("only one of '--recovery' and '--boot-entry' can be set") + } + + upgradeEntry := "" + if c.Bool("recovery") { + upgradeEntry = constants.BootEntryRecovery + } else if c.String("boot-entry") != "" { + upgradeEntry = c.String("boot-entry") + } + return agent.Upgrade(source, c.Bool("force"), c.Bool("strict-validation"), constants.GetConfigScanDirs(), - c.String("single-entry"), - c.Bool("pre"), c.Bool("recovery"), + upgradeEntry, c.Bool("pre"), ) }, }, diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 694c3e22..73af2e38 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -26,7 +26,7 @@ import ( "github.com/kairos-io/kairos-agent/v2/pkg/elemental" v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" "github.com/kairos-io/kairos-agent/v2/pkg/utils" - "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs" + fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs" "github.com/kairos-io/kairos-sdk/state" ) @@ -90,7 +90,7 @@ func (u *UpgradeAction) upgradeInstallStateYaml(meta interface{}, img v1.Image) Label: img.Label, FS: img.FS, } - if u.spec.RecoveryUpgrade { + if u.spec.RecoveryUpgrade() { recoveryPart := u.spec.State.Partitions[constants.RecoveryPartName] if recoveryPart == nil { recoveryPart = &v1.PartitionState{ @@ -137,7 +137,7 @@ func (u *UpgradeAction) Run() (err error) { e := elemental.NewElemental(u.config) - if u.spec.RecoveryUpgrade { + if u.spec.RecoveryUpgrade() { upgradeImg = u.spec.Recovery if upgradeImg.FS == constants.SquashFs { finalImageFile = filepath.Join(u.spec.Partitions.Recovery.MountPoint, "cOS", constants.RecoverySquashFile) @@ -227,7 +227,7 @@ func (u *UpgradeAction) Run() (err error) { } // Only apply rebrand stage for system upgrades - if !u.spec.RecoveryUpgrade { + if !u.spec.RecoveryUpgrade() { u.Info("rebranding") if rebrandingErr := e.SetDefaultGrubEntry(u.spec.Partitions.State.MountPoint, upgradeImg.MountPoint, u.spec.GrubDefEntry); rebrandingErr != nil { u.config.Logger.Warn("failure while rebranding GRUB default entry (ignoring), run with --debug to see more details") @@ -244,7 +244,7 @@ func (u *UpgradeAction) Run() (err error) { // If not upgrading recovery and booting from non passive, backup active into passive // We dont want to overwrite passive if we are booting from passive as it could mean that active is broken and we would // be overriding a working passive with a broken/unknown active - if u.spec.RecoveryUpgrade == false && bootedFrom != state.Passive { + if !u.spec.RecoveryUpgrade() && bootedFrom != state.Passive { // backup current active.img to passive.img before overwriting the active.img u.Info("Backing up current active image") source := filepath.Join(u.spec.Partitions.State.MountPoint, "cOS", constants.ActiveImgFile) @@ -289,7 +289,7 @@ func (u *UpgradeAction) Run() (err error) { } u.Info("Upgrade completed") - if !u.spec.RecoveryUpgrade { + if !u.spec.RecoveryUpgrade() { u.config.Logger.Warn("Remember that recovery is upgraded separately by passing the --recovery flag to the upgrade command!\n" + "See more info about this on https://kairos.io/docs/upgrade/") } diff --git a/pkg/config/spec.go b/pkg/config/spec.go index 8b1196c3..af36ef07 100644 --- a/pkg/config/spec.go +++ b/pkg/config/spec.go @@ -346,7 +346,7 @@ func setUpgradeSourceSize(cfg *Config, spec *v1.UpgradeSpec) error { var err error var targetSpec *v1.Image - if spec.RecoveryUpgrade { + if spec.RecoveryUpgrade() { targetSpec = &(spec.Recovery) } else { targetSpec = &(spec.Active) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index fd57ca16..4fe6f017 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -88,6 +88,7 @@ const ( GPT = "gpt" UsrLocalPath = "/usr/local" OEMPath = "/oem" + BootEntryRecovery = "recovery" // SELinux targeted policy paths SELinuxTargetedPath = "/etc/selinux/targeted" diff --git a/pkg/types/v1/config.go b/pkg/types/v1/config.go index 49e32606..7d4f6469 100644 --- a/pkg/types/v1/config.go +++ b/pkg/types/v1/config.go @@ -170,7 +170,7 @@ func (r *ResetSpec) ShouldReboot() bool { return r.Reboot } func (r *ResetSpec) ShouldShutdown() bool { return r.PowerOff } type UpgradeSpec struct { - RecoveryUpgrade bool `yaml:"recovery,omitempty" mapstructure:"recovery"` + Entry string `yaml:"entry,omitempty" mapstructure:"entry"` Active Image `yaml:"system,omitempty" mapstructure:"system"` Recovery Image `yaml:"recovery-system,omitempty" mapstructure:"recovery-system"` GrubDefEntry string `yaml:"grub-entry-name,omitempty" mapstructure:"grub-entry-name"` @@ -182,10 +182,14 @@ type UpgradeSpec struct { State *InstallState } +func (u *UpgradeSpec) RecoveryUpgrade() bool { + return u.Entry == constants.BootEntryRecovery +} + // Sanitize checks the consistency of the struct, returns error // if unsolvable inconsistencies are found func (u *UpgradeSpec) Sanitize() error { - if u.RecoveryUpgrade { + if u.RecoveryUpgrade() { if u.Recovery.Source.IsEmpty() { return fmt.Errorf(constants.UpgradeNoSourceError) } @@ -528,12 +532,15 @@ func (i *InstallUkiSpec) GetPartitions() ElementalPartitions { return i.Partitio func (i *InstallUkiSpec) GetExtraPartitions() PartitionList { return i.ExtraPartitions } type UpgradeUkiSpec struct { - RecoveryUpgrade bool `yaml:"recovery,omitempty" mapstructure:"recovery"` - UpgradeSingleEntry string `yaml:"upgrade-single-entry,omitempty" mapstructure:"upgrade-single-entry"` - Active Image `yaml:"system,omitempty" mapstructure:"system"` - Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` - PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` - EfiPartition *Partition `yaml:"efi-partition,omitempty" mapstructure:"efi-partition"` + Entry string `yaml:"entry,omitempty" mapstructure:"entry"` + Active Image `yaml:"system,omitempty" mapstructure:"system"` + Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"` + PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"` + EfiPartition *Partition `yaml:"efi-partition,omitempty" mapstructure:"efi-partition"` +} + +func (i *UpgradeUkiSpec) RecoveryUpgrade() bool { + return i.Entry == constants.BootEntryRecovery } func (i *UpgradeUkiSpec) Sanitize() error { diff --git a/pkg/uki/upgrade.go b/pkg/uki/upgrade.go index 90642838..7b369ed2 100644 --- a/pkg/uki/upgrade.go +++ b/pkg/uki/upgrade.go @@ -51,17 +51,20 @@ func (i *UpgradeAction) Run() (err error) { // If we decide to first copy and then rotate, we need ~4 times the size of // the artifact set [TBD] - // When upgrading recovery, we don't want to replace loader.conf or any other + // When upgrading recovery or single entries, we don't want to replace loader.conf or any other // files, thus we take a simpler approach and only install the new efi file // and the relevant conf - if i.spec.RecoveryUpgrade { + if i.spec.RecoveryUpgrade() { + i.cfg.Logger.Infof("installing entry: recovery") return i.installRecovery() } - if i.spec.UpgradeSingleEntry != "" { - return i.installEntry(i.spec.UpgradeSingleEntry) + if i.spec.Entry != "" { // single entry upgrade + i.cfg.Logger.Infof("installing entry: %s", i.spec.Entry) + return i.installEntry(i.spec.Entry) } + i.cfg.Logger.Infof("installing entry: active") // Dump artifact to efi dir _, err = e.DumpSource(constants.UkiEfiDir, i.spec.Active.Source) if err != nil { @@ -127,6 +130,11 @@ func (i *UpgradeAction) Run() (err error) { } func (i *UpgradeAction) installEntry(entry string) error { + targetEntryFile := filepath.Join(constants.UkiEfiDir, "EFI", "kairos", fmt.Sprintf("%s.efi", entry)) + if _, err := os.Stat(targetEntryFile); err != nil { + return fmt.Errorf("could not stat target efi file for entry %s: %s", entry, err) + } + tmpDir, err := os.MkdirTemp("", "") if err != nil { i.cfg.Logger.Errorf("creating a tmp dir: %s", err.Error()) @@ -142,9 +150,7 @@ func (i *UpgradeAction) installEntry(entry string) error { return err } - err = copyFile( - filepath.Join(tmpDir, "EFI", "kairos", UnassignedArtifactRole+".efi"), - filepath.Join(constants.UkiEfiDir, "EFI", "kairos", fmt.Sprintf("%s.efi", entry))) + err = copyFile(filepath.Join(tmpDir, "EFI", "kairos", UnassignedArtifactRole+".efi"), targetEntryFile) if err != nil { i.cfg.Logger.Errorf("copying efi files: %s", err.Error()) return err From c0c419cce8255b22b69e5387987e36319d458d0e Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Tue, 13 Aug 2024 14:51:42 +0300 Subject: [PATCH 5/7] Fix unit tests Signed-off-by: Dimitris Karakasilis --- pkg/action/upgrade_test.go | 9 ++++----- pkg/types/v1/config_test.go | 5 +++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go index a53edd6e..3254716a 100644 --- a/pkg/action/upgrade_test.go +++ b/pkg/action/upgrade_test.go @@ -19,9 +19,10 @@ package action_test import ( "bytes" "fmt" - sdkTypes "github.com/kairos-io/kairos-sdk/types" "path/filepath" + sdkTypes "github.com/kairos-io/kairos-sdk/types" + agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config" fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs" @@ -449,8 +450,7 @@ var _ = Describe("Runtime Actions", func() { spec.Active.Size = 10 spec.Passive.Size = 10 spec.Recovery.Size = 10 - - spec.RecoveryUpgrade = true + spec.Entry = constants.BootEntryRecovery err = fsutils.MkdirAll(config.Fs, "/proc", constants.DirPerm) Expect(err).ShouldNot(HaveOccurred()) @@ -542,8 +542,7 @@ var _ = Describe("Runtime Actions", func() { spec.Active.Size = 10 spec.Passive.Size = 10 spec.Recovery.Size = 10 - - spec.RecoveryUpgrade = true + spec.Entry = constants.BootEntryRecovery err = fsutils.MkdirAll(config.Fs, "/proc", constants.DirPerm) Expect(err).ShouldNot(HaveOccurred()) diff --git a/pkg/types/v1/config_test.go b/pkg/types/v1/config_test.go index 877e7c1b..04dd2236 100644 --- a/pkg/types/v1/config_test.go +++ b/pkg/types/v1/config_test.go @@ -17,11 +17,12 @@ limitations under the License. package v1_test import ( + "path/filepath" + "github.com/kairos-io/kairos-agent/v2/pkg/constants" v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "path/filepath" ) var _ = Describe("Types", Label("types", "config"), func() { @@ -444,7 +445,7 @@ var _ = Describe("Types", Label("types", "config"), func() { }) Describe("Recovery upgrade", func() { BeforeEach(func() { - spec.RecoveryUpgrade = true + spec.Entry = constants.BootEntryRecovery }) It("fails with empty source", func() { err := spec.Sanitize() From 0e8195b7a4c8b150a4f56f935ea128775fe4bd2b Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Tue, 13 Aug 2024 15:12:44 +0300 Subject: [PATCH 6/7] Fix other test Signed-off-by: Dimitris Karakasilis --- pkg/config/spec_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/config/spec_test.go b/pkg/config/spec_test.go index e35e2022..07c5a821 100644 --- a/pkg/config/spec_test.go +++ b/pkg/config/spec_test.go @@ -18,11 +18,12 @@ package config_test import ( "fmt" - sdkTypes "github.com/kairos-io/kairos-sdk/types" - "github.com/rs/zerolog" "os" "path/filepath" + sdkTypes "github.com/kairos-io/kairos-sdk/types" + "github.com/rs/zerolog" + "github.com/jaypipes/ghw/pkg/block" config "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/constants" @@ -587,7 +588,7 @@ cloud-init-paths: spec, err := config.ReadSpecFromCloudConfig(cfg, "upgrade") Expect(err).ToNot(HaveOccurred()) upgradeSpec := spec.(*v1.UpgradeSpec) - Expect(upgradeSpec.RecoveryUpgrade).To(BeTrue()) + Expect(upgradeSpec.RecoveryUpgrade()).To(BeTrue()) }) It("Fails when a wrong action is read", func() { cfg, err := config.Scan(collector.Directories([]string{dir}...), collector.NoLogs) From 332d5164678916a68da5c377b5cb331c08f1d4c1 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Tue, 13 Aug 2024 15:48:02 +0300 Subject: [PATCH 7/7] Respect upgrade.recovery when set through the config file Signed-off-by: Dimitris Karakasilis --- pkg/config/spec.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/config/spec.go b/pkg/config/spec.go index af36ef07..a59ef8e6 100644 --- a/pkg/config/spec.go +++ b/pkg/config/spec.go @@ -24,6 +24,7 @@ import ( "reflect" "strings" + "github.com/kairos-io/kairos-sdk/collector" sdkTypes "github.com/kairos-io/kairos-sdk/types" "github.com/google/go-containerregistry/pkg/crane" @@ -323,7 +324,21 @@ func NewUpgradeSpec(cfg *Config) (*v1.UpgradeSpec, error) { } } + // Deep look to see if upgrade.recovery == true in the config + // if yes, we set the upgrade spec "Entry" to "recovery" + entry := "" + _, ok := cfg.Config["upgrade"] + if ok { + _, ok = cfg.Config["upgrade"].(collector.Config)["recovery"] + if ok { + if cfg.Config["upgrade"].(collector.Config)["recovery"].(bool) { + entry = constants.BootEntryRecovery + } + } + } + spec := &v1.UpgradeSpec{ + Entry: entry, Active: active, Recovery: recovery, Passive: passive,