mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-06-03 01:44:53 +00:00
Uki upgrade (#182)
This commit is contained in:
parent
2be11b827e
commit
3254b8a36e
3
go.mod
3
go.mod
@ -13,8 +13,8 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/jaypipes/ghw v0.12.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kairos-io/kairos-sdk v0.0.20
|
||||
github.com/kairos-io/kcrypt v0.8.0
|
||||
github.com/kairos-io/kairos-sdk v0.0.20
|
||||
github.com/labstack/echo/v4 v4.11.1
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb
|
||||
@ -161,6 +161,7 @@ require (
|
||||
github.com/swaggest/refl v1.3.0 // indirect
|
||||
github.com/tredoe/osutil/v2 v2.0.0-rc.16 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/urfave/cli v1.22.14 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -46,6 +46,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
|
||||
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
|
||||
@ -621,6 +622,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
|
||||
@ -639,6 +641,8 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
|
||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
||||
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
|
||||
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
|
@ -9,12 +9,37 @@ import (
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
kcrypt "github.com/kairos-io/kcrypt/pkg/lib"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type KcryptUKI struct{}
|
||||
|
||||
func (k KcryptUKI) Run(c config.Config, _ v1.Spec) error {
|
||||
// pre-check for systemd version, we need something higher or equal to 252
|
||||
run, err := utils.SH("systemctl --version | head -1 | awk '{ print $2}'")
|
||||
systemdVersion := strings.TrimSpace(string(run))
|
||||
if err != nil {
|
||||
c.Logger.Errorf("could not get systemd version: %s", err)
|
||||
c.Logger.Errorf("could not get systemd version: %s", run)
|
||||
return err
|
||||
}
|
||||
if systemdVersion == "" {
|
||||
c.Logger.Errorf("could not get systemd version: %s", err)
|
||||
return err
|
||||
}
|
||||
// Change systemdVersion to int value
|
||||
systemdVersionInt, err := strconv.Atoi(systemdVersion)
|
||||
if err != nil {
|
||||
c.Logger.Errorf("could not convert systemd version to int: %s", err)
|
||||
return err
|
||||
}
|
||||
// If systemd version is less than 252 return
|
||||
if systemdVersionInt < 252 {
|
||||
c.Logger.Infof("systemd version is %s, we need 252 or higher for encrypting partitions", systemdVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// We always encrypt OEM and PERSISTENT under UKI
|
||||
// If mounted, unmount it
|
||||
@ -22,7 +47,7 @@ func (k KcryptUKI) Run(c config.Config, _ v1.Spec) error {
|
||||
_ = machine.Umount(constants.PersistentDir) //nolint:errcheck
|
||||
|
||||
// Backup oem as we already copied files on there and on luksify it will be wiped
|
||||
err := machine.Mount("COS_OEM", constants.OEMDir)
|
||||
err = machine.Mount("COS_OEM", constants.OEMDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -60,7 +85,6 @@ func (k KcryptUKI) Run(c config.Config, _ v1.Spec) error {
|
||||
c.Logger.Infof("Done encrypting %s", p)
|
||||
}
|
||||
|
||||
// Restore OEM
|
||||
err = kcrypt.UnlockAll(true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/action"
|
||||
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/uki"
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
@ -185,3 +186,49 @@ func getReleasesFromProvider(includePrereleases bool) ([]string, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func UkiUpgrade(source string, dirs []string, strictValidations bool) error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
cliConf, err := generateUpgradeConfForCLIArgs(source, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...),
|
||||
collector.Readers(strings.NewReader(cliConf)),
|
||||
collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
// Load the upgrade Config from the system
|
||||
upgradeSpec, err := config.ReadUkiUpgradeFromConfig(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = upgradeSpec.Sanitize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeAction := uki.NewUpgradeAction(c, upgradeSpec)
|
||||
|
||||
err = upgradeAction.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if upgradeSpec.Reboot {
|
||||
utils.Reboot()
|
||||
}
|
||||
|
||||
if upgradeSpec.PowerOff {
|
||||
utils.PowerOFF()
|
||||
}
|
||||
|
||||
return hook.Run(*c, upgradeSpec, hook.AfterUpgrade...)
|
||||
}
|
||||
|
30
main.go
30
main.go
@ -27,6 +27,7 @@ import (
|
||||
"github.com/kairos-io/kairos-sdk/schema"
|
||||
"github.com/kairos-io/kairos-sdk/state"
|
||||
"github.com/kairos-io/kairos-sdk/versioneer"
|
||||
"github.com/sanity-io/litter"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
@ -726,6 +727,12 @@ The validate command expects a configuration file as its only argument. Local fi
|
||||
Name: "device",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
if c.String("device") == "" {
|
||||
return fmt.Errorf("on uki, --device flag is required")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs)
|
||||
if err != nil {
|
||||
@ -757,22 +764,14 @@ The validate command expects a configuration file as its only argument. Local fi
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
if c.String("source") == "" {
|
||||
return fmt.Errorf("On uki, source is required")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
config, err := agentConfig.Scan(collector.Directories(configScanDir...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the spec from the config
|
||||
upgradeSpec, err := agentConfig.ReadUkiUpgradeFromConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeAction := uki.NewUpgradeAction(config, upgradeSpec)
|
||||
return upgradeAction.Run()
|
||||
source := c.String("source")
|
||||
return agent.UkiUpgrade(source, configScanDir, c.Bool("strict-validation"))
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -847,6 +846,9 @@ The kairos agent is a component to abstract away node ops, providing a common fe
|
||||
Before: func(c *cli.Context) error {
|
||||
// Set debug from here already, so it's loaded by the ReadConfigRun
|
||||
viper.Set("debug", c.Bool("debug"))
|
||||
if c.Bool("debug") {
|
||||
litter.Config.HidePrivateFields = false
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Commands: cmds,
|
||||
|
@ -496,7 +496,7 @@ func NewUkiInstallSpec(cfg *Config) (*v1.InstallUkiSpec, error) {
|
||||
// Calculate the partitions afterwards so they use the image sizes for the final partition sizes
|
||||
spec.Partitions.EFI = &v1.Partition{
|
||||
FilesystemLabel: constants.EfiLabel,
|
||||
Size: constants.ImgSize, // TODO: Fix this and set proper size based on the source size
|
||||
Size: constants.ImgSize * 2, // TODO: Fix this and set proper size based on the source size
|
||||
Name: constants.EfiPartName,
|
||||
FS: constants.EfiFs,
|
||||
MountPoint: constants.EfiDir,
|
||||
@ -521,7 +521,7 @@ func NewUkiInstallSpec(cfg *Config) (*v1.InstallUkiSpec, error) {
|
||||
|
||||
// TODO: Which key to use? install or install-uki?
|
||||
err := unmarshallFullSpec(cfg, "install", spec)
|
||||
|
||||
// TODO: Get the actual source size to calculate the image size and partitions size for at least 3 UKI images
|
||||
return spec, err
|
||||
}
|
||||
|
||||
@ -535,6 +535,28 @@ func ReadUkiInstallSpecFromConfig(c *Config) (*v1.InstallUkiSpec, error) {
|
||||
return installSpec, nil
|
||||
}
|
||||
|
||||
func NewUkiUpgradeSpec(cfg *Config) (*v1.UpgradeUkiSpec, error) {
|
||||
spec := &v1.UpgradeUkiSpec{}
|
||||
err := unmarshallFullSpec(cfg, "upgrade", spec)
|
||||
// Get the actual source size to calculate the image size and partitions size
|
||||
size, err := GetSourceSize(cfg, spec.Active.Source)
|
||||
if err != nil {
|
||||
cfg.Logger.Warnf("Failed to infer size for images: %s", err.Error())
|
||||
spec.Active.Size = constants.ImgSize
|
||||
} else {
|
||||
cfg.Logger.Infof("Setting image size to %dMb", size)
|
||||
spec.Active.Size = uint(size)
|
||||
}
|
||||
|
||||
spec.EfiPartition = &v1.Partition{
|
||||
FilesystemLabel: constants.EfiLabel,
|
||||
FS: constants.EfiFs,
|
||||
Path: constants.UkiEfiDiskByLabel,
|
||||
MountPoint: constants.UkiEfiDir,
|
||||
}
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// ReadUkiUpgradeFromConfig will return a proper v1.UpgradeUkiSpec based on an agent Config
|
||||
func ReadUkiUpgradeFromConfig(c *Config) (*v1.UpgradeUkiSpec, error) {
|
||||
sp, err := ReadSpecFromCloudConfig(c, "upgrade-uki")
|
||||
@ -653,8 +675,7 @@ func ReadSpecFromCloudConfig(r *Config, spec string) (v1.Spec, error) {
|
||||
// TODO: Fill with proper defaults
|
||||
sp = &v1.ResetUkiSpec{}
|
||||
case "upgrade-uki":
|
||||
// TODO: Fill with proper defaults
|
||||
sp = &v1.UpgradeUkiSpec{}
|
||||
sp, err = NewUkiUpgradeSpec(r)
|
||||
default:
|
||||
return nil, fmt.Errorf("spec not valid: %s", spec)
|
||||
}
|
||||
|
@ -108,8 +108,10 @@ const (
|
||||
|
||||
Rsync = "rsync"
|
||||
|
||||
UkiSource = "/run/install/uki"
|
||||
UkiCdromSource = "/run/install/cdrom"
|
||||
UkiSource = "/run/install/uki"
|
||||
UkiCdromSource = "/run/install/cdrom"
|
||||
UkiEfiDir = "/efi"
|
||||
UkiEfiDiskByLabel = `/dev/disk/by-label/` + EfiLabel
|
||||
)
|
||||
|
||||
func GetCloudInitPaths() []string {
|
||||
|
@ -35,7 +35,7 @@ const (
|
||||
|
||||
// ImageSource represents the source from where an image is created for easy identification
|
||||
type ImageSource struct {
|
||||
source string
|
||||
source string `yaml:"source"`
|
||||
srcType string
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,6 @@ func (u *UpgradeSpec) Sanitize() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UpgradeSpec) ShouldReboot() bool { return u.Reboot }
|
||||
func (u *UpgradeSpec) ShouldShutdown() bool { return u.PowerOff }
|
||||
|
||||
@ -526,8 +525,10 @@ func (i *InstallUkiSpec) GetPartitions() ElementalPartitions { return i.Partitio
|
||||
func (i *InstallUkiSpec) GetExtraPartitions() PartitionList { return i.ExtraPartitions }
|
||||
|
||||
type UpgradeUkiSpec struct {
|
||||
Reboot bool `yaml:"reboot,omitempty" mapstructure:"reboot"`
|
||||
PowerOff bool `yaml:"poweroff,omitempty" mapstructure:"poweroff"`
|
||||
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 {
|
||||
|
@ -3,9 +3,12 @@ package uki
|
||||
import (
|
||||
hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks"
|
||||
"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/elemental"
|
||||
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"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
)
|
||||
|
||||
type UpgradeAction struct {
|
||||
@ -18,16 +21,30 @@ func NewUpgradeAction(cfg *config.Config, spec *v1.UpgradeUkiSpec) *UpgradeActio
|
||||
}
|
||||
|
||||
func (i *UpgradeAction) Run() (err error) {
|
||||
e := elemental.NewElemental(i.cfg)
|
||||
cleanup := utils.NewCleanStack()
|
||||
defer func() { err = cleanup.Cleanup(err) }()
|
||||
// Run pre-install stage
|
||||
_ = elementalUtils.RunStage(i.cfg, "kairos-uki-upgrade.pre")
|
||||
_ = events.RunHookScript("/usr/bin/kairos-agent.uki.upgrade.pre.hook")
|
||||
|
||||
// Get source (from spec?)
|
||||
// Copy the efi file into the proper dir
|
||||
// Remove all boot manager entries?
|
||||
// Create boot manager entry
|
||||
// Set default entry to the one we just created
|
||||
|
||||
// REMOUNT /efi as RW (its RO by default)
|
||||
umount, err := e.MountRWPartition(i.spec.EfiPartition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cleanup.Push(umount)
|
||||
// TODO: Check size of EFI partition to see if we can upgrade
|
||||
// TODO: Check size of source to see if we can upgrade
|
||||
// TODO: Check number of existing UKI files
|
||||
// TODO: Load them, order them via semver
|
||||
// TODO: Remove the latest one if its over the max number of entries
|
||||
// Dump artifact to efi dir
|
||||
_, err = e.DumpSource(constants.UkiEfiDir, i.spec.Active.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = elementalUtils.RunStage(i.cfg, "kairos-uki-upgrade.after")
|
||||
_ = events.RunHookScript("/usr/bin/kairos-agent.uki.upgrade.after.hook") //nolint:errcheck
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user