Run install off the same command (#196)

This commit is contained in:
Itxaka 2024-01-09 15:10:04 +01:00 committed by GitHub
parent 663801091d
commit 775756f4b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 86 deletions

View File

@ -5,6 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/kairos-io/kairos-agent/v2/pkg/uki"
internalutils "github.com/kairos-io/kairos-agent/v2/pkg/utils"
"net/url"
"os"
"strings"
@ -14,13 +16,10 @@ import (
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
"github.com/sanity-io/litter"
hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks"
"github.com/kairos-io/kairos-agent/v2/internal/bus"
"github.com/kairos-io/kairos-agent/v2/internal/cmd"
"github.com/kairos-io/kairos-agent/v2/pkg/action"
"github.com/kairos-io/kairos-agent/v2/pkg/config"
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/collector"
"github.com/kairos-io/kairos-sdk/machine"
@ -217,41 +216,27 @@ func RunInstall(c *config.Config) error {
c.Install.Device = detectDevice()
}
// Load the installation spec from the Config
installSpec, err := config.ReadInstallSpecFromConfig(c)
if err != nil {
return err
// UKI path. Check if we are on UKI AND if we are running off a cd, otherwise it makes no sense to run the install
// From the installed system
if internalutils.UkiBootMode() == internalutils.UkiRemovableMedia {
return runInstallUki(c)
} else { // Non-uki path
return runInstall(c)
}
}
f, err := fsutils.TempFile(c.Fs, "", "kairos-install-config-xxx.yaml")
if err != nil {
c.Logger.Error("Error creating temporary file for install config: %s\n", err.Error())
return err
}
defer os.RemoveAll(f.Name())
ccstring, err := c.String()
// runInstallUki runs the UKI path install
func runInstallUki(c *config.Config) error {
// Load the spec from the config
installSpec, err := config.ReadUkiInstallSpecFromConfig(c)
if err != nil {
return err
}
err = os.WriteFile(f.Name(), []byte(ccstring), os.ModePerm)
if err != nil {
fmt.Printf("could not write cloud init to %s: %s\n", f.Name(), err.Error())
return err
}
// TODO: This should not be neccessary
installSpec.NoFormat = c.Install.NoFormat
// Set our cloud-init to the file we just created
installSpec.CloudInit = append(installSpec.CloudInit, f.Name())
// Get the source of the installation if we are overriding it
if c.Install.Image != "" {
imgSource, err := v1.NewSrcFromURI(c.Install.Image)
if err != nil {
return err
}
installSpec.Active.Source = imgSource
f, err := dumpCCStringToFile(c)
if err == nil {
installSpec.CloudInit = append(installSpec.CloudInit, f)
}
// Check if values are correct
@ -263,20 +248,59 @@ func RunInstall(c *config.Config) error {
// Add user's cloud-config (to run user defined "before-install" stages)
c.CloudInitPaths = append(c.CloudInitPaths, installSpec.CloudInit...)
// Run pre-install stage
_ = elementalUtils.RunStage(c, "kairos-install.pre")
events.RunHookScript("/usr/bin/kairos-agent.install.pre.hook") //nolint:errcheck
// Create the action
installAction := action.NewInstallAction(c, installSpec)
// Run it
if err := installAction.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
_ = elementalUtils.RunStage(c, "kairos-install.after")
events.RunHookScript("/usr/bin/kairos-agent.install.after.hook") //nolint:errcheck
installAction := uki.NewInstallAction(c, installSpec)
return installAction.Run()
}
return hook.Run(*c, installSpec, hook.AfterInstall...)
// runInstall runs the non-UKI path install
func runInstall(c *config.Config) error {
// Load the installation spec from the Config
installSpec, err := config.ReadInstallSpecFromConfig(c)
if err != nil {
return err
}
// TODO: This should not be neccessary
installSpec.NoFormat = c.Install.NoFormat
// Set our cloud-init to the file we just created
f, err := dumpCCStringToFile(c)
if err == nil {
installSpec.CloudInit = append(installSpec.CloudInit, f)
}
// Check if values are correct
err = installSpec.Sanitize()
if err != nil {
return err
}
// Add user's cloud-config (to run user defined "before-install" stages)
c.CloudInitPaths = append(c.CloudInitPaths, installSpec.CloudInit...)
installAction := action.NewInstallAction(c, installSpec)
return installAction.Run()
}
// dumpCCStringToFile dumps the cloud-init string to a file and returns the path of the file
func dumpCCStringToFile(c *config.Config) (string, error) {
f, err := fsutils.TempFile(c.Fs, "", "kairos-install-config-xxx.yaml")
if err != nil {
c.Logger.Error("Error creating temporary file for install config: %s\n", err.Error())
return "", err
}
defer os.RemoveAll(f.Name())
ccstring, err := c.String()
if err != nil {
return "", err
}
err = os.WriteFile(f.Name(), []byte(ccstring), os.ModePerm)
if err != nil {
fmt.Printf("could not write cloud init to %s: %s\n", f.Name(), err.Error())
return "", err
}
return f.Name(), nil
}
func ensureDataSourceReady() {

41
main.go
View File

@ -711,47 +711,6 @@ The validate command expects a configuration file as its only argument. Local fi
// command level: kairos-agent uki --source oci:whatever install
// subcommand level: kairos-agent uki install --source oci:whatever
Subcommands: []*cli.Command{
{
Name: "install",
Usage: "Install to disk",
UsageText: "install [--device DEVICE]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "source",
Usage: "Source for install. Composed of `type:address`. Accepts `file:`,`dir:` or `oci:` for the type of source.\nFor example `file:/var/share/myimage.tar`, `dir:/tmp/extracted` or `oci:repo/image:tag`",
Action: func(c *cli.Context, s string) error {
return validateSource(s)
},
},
&cli.StringFlag{
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 {
return err
}
// Load the spec from the config
installSpec, err := agentConfig.ReadUkiInstallSpecFromConfig(config)
if err != nil {
return err
}
if c.String("device") != "" {
installSpec.Target = c.String("device")
}
installAction := uki.NewInstallAction(config, installSpec)
return installAction.Run()
},
},
{
Name: "upgrade",
Flags: []cli.Flag{

View File

@ -18,7 +18,9 @@ package action
import (
"fmt"
hook "github.com/kairos-io/kairos-agent/v2/internal/agent/hooks"
"github.com/kairos-io/kairos-agent/v2/pkg/config"
events "github.com/kairos-io/kairos-sdk/bus"
"path/filepath"
"strings"
"time"
@ -123,6 +125,10 @@ func (i InstallAction) Run() (err error) {
cleanup := utils.NewCleanStack()
defer func() { err = cleanup.Cleanup(err) }()
// Run pre-install stage
_ = utils.RunStage(i.cfg, "kairos-install.pre")
events.RunHookScript("/usr/bin/kairos-agent.install.pre.hook") //nolint:errcheck
// Set installation sources from a downloaded ISO
if i.spec.Iso != "" {
tmpDir, err := e.GetIso(i.spec.Iso)
@ -275,5 +281,8 @@ func (i InstallAction) Run() (err error) {
}
}
return err
_ = utils.RunStage(i.cfg, "kairos-install.after")
_ = events.RunHookScript("/usr/bin/kairos-agent.install.after.hook") //nolint:errcheck
return hook.Run(*i.cfg, i.spec, hook.AfterInstall...)
}

View File

@ -20,6 +20,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"github.com/kairos-io/kairos-sdk/state"
"io"
random "math/rand"
"net/url"
@ -498,3 +499,31 @@ func FindCommand(defaultPath string, options []string) string {
// Otherwise return default
return defaultPath
}
// IsUki returns true if the system is running in UKI mode. Checks the cmdline as UKI artifacts have the rd.immucore.uki flag
func IsUki() bool {
cmdline, _ := os.ReadFile("/proc/cmdline")
if strings.Contains(string(cmdline), "rd.immucore.uki") {
return true
}
return false
}
const (
UkiHDD state.Boot = "uki_boot_mode"
UkiRemovableMedia state.Boot = "uki_install_mode"
)
// UkiBootMode will return where the system is running from, either HDD or RemovableMedia
// HDD means we are booting from an already installed system
// RemovableMedia means we are booting from a live media like a CD or USB
func UkiBootMode() state.Boot {
if IsUki() {
_, err := os.Stat("/run/cos/uki_boot_mode")
if err != nil {
return UkiHDD
}
return UkiRemovableMedia
}
return state.Unknown
}