kairos-agent/pkg/uki/upgrade.go

127 lines
3.8 KiB
Go
Raw Normal View History

2023-10-03 09:15:17 +00:00
package uki
import (
"fmt"
"os"
"path/filepath"
"github.com/kairos-io/kairos-agent/v2/pkg/action"
2023-10-03 09:15:17 +00:00
"github.com/kairos-io/kairos-agent/v2/pkg/config"
2023-12-18 10:38:26 +00:00
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
"github.com/kairos-io/kairos-agent/v2/pkg/elemental"
2023-10-03 09:15:17 +00:00
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"
2023-12-18 10:38:26 +00:00
"github.com/kairos-io/kairos-sdk/utils"
2023-10-03 09:15:17 +00:00
)
type UpgradeAction struct {
cfg *config.Config
spec *v1.UpgradeUkiSpec
}
func NewUpgradeAction(cfg *config.Config, spec *v1.UpgradeUkiSpec) *UpgradeAction {
return &UpgradeAction{cfg: cfg, spec: spec}
}
func (i *UpgradeAction) Run() (err error) {
2023-12-18 10:38:26 +00:00
e := elemental.NewElemental(i.cfg)
cleanup := utils.NewCleanStack()
defer func() { err = cleanup.Cleanup(err) }()
2023-10-03 09:15:17 +00:00
// Run pre-install stage
_ = elementalUtils.RunStage(i.cfg, "kairos-uki-upgrade.pre")
_ = events.RunHookScript("/usr/bin/kairos-agent.uki.upgrade.pre.hook")
2023-12-18 10:38:26 +00:00
// REMOUNT /efi as RW (its RO by default)
umount, err := e.MountRWPartition(i.spec.EfiPartition)
if err != nil {
return err
}
cleanup.Push(umount)
// TODO: Get the size of the efi partition and decide if the images can fit
// before trying the upgrade.
// 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
// files, thus we take a simpler approach and only install the new efi file
// and the relevant conf
if i.spec.RecoveryUpgrade {
return i.installRecovery()
}
// Dump artifact to efi dir
_, err = e.DumpSource(constants.UkiEfiDir, i.spec.Active.Source)
2023-12-18 15:09:55 +00:00
if err != nil {
return err
}
// Rotate first
err = overwriteArtifactSetRole(i.cfg.Fs, constants.UkiEfiDir, "active", "passive", i.cfg.Logger)
if err != nil {
return fmt.Errorf("rotating active to passive: %w", err)
2023-12-18 15:09:55 +00:00
}
// Install the new artifacts as "active"
err = overwriteArtifactSetRole(i.cfg.Fs, constants.UkiEfiDir, UnassignedArtifactRole, "active", i.cfg.Logger)
2023-12-18 10:38:26 +00:00
if err != nil {
return fmt.Errorf("installing the new artifacts as active: %w", err)
}
loaderConfPath := filepath.Join(constants.UkiEfiDir, "loader", "loader.conf")
if err = replaceRoleInKey(loaderConfPath, "default", UnassignedArtifactRole, "active", i.cfg.Logger); err != nil {
2023-12-18 10:38:26 +00:00
return err
}
2023-12-18 15:09:55 +00:00
if err = removeArtifactSetWithRole(i.cfg.Fs, constants.UkiEfiDir, UnassignedArtifactRole); err != nil {
return fmt.Errorf("removing artifact set: %w", err)
}
// SelectBootEntry sets the default boot entry to the selected entry
err = action.SelectBootEntry(i.cfg, "active")
// Should we fail? Or warn?
if err != nil {
return err
}
2023-10-03 09:15:17 +00:00
_ = elementalUtils.RunStage(i.cfg, "kairos-uki-upgrade.after")
_ = events.RunHookScript("/usr/bin/kairos-agent.uki.upgrade.after.hook") //nolint:errcheck
2024-02-02 12:20:06 +00:00
return nil
2023-10-03 09:15:17 +00:00
}
// installRecovery replaces the "recovery" role efi and conf files with
// the UnassignedArtifactRole efi and loader files from dir
func (i *UpgradeAction) installRecovery() error {
tmpDir, err := os.MkdirTemp("", "")
if err != nil {
return fmt.Errorf("creating a tmp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
// Dump artifact to tmp dir
e := elemental.NewElemental(i.cfg)
_, err = e.DumpSource(tmpDir, i.spec.Active.Source)
if err != nil {
return err
}
err = copyFile(
filepath.Join(tmpDir, "EFI", "kairos", UnassignedArtifactRole+".efi"),
filepath.Join(constants.UkiEfiDir, "EFI", "kairos", "recovery.efi"))
if err != nil {
return err
}
targetConfPath := filepath.Join(constants.UkiEfiDir, "loader", "entries", "recovery.conf")
err = copyFile(
filepath.Join(tmpDir, "loader", "entries", UnassignedArtifactRole+".conf"),
targetConfPath)
if err != nil {
return err
}
return replaceRoleInKey(targetConfPath, "efi", UnassignedArtifactRole, "recovery", i.cfg.Logger)
}