From 7d18e6504a7e54d554bba6801d6a4860da67c884 Mon Sep 17 00:00:00 2001 From: Dimitris Karakasilis Date: Tue, 26 Sep 2023 17:09:24 +0300 Subject: [PATCH] [WIP] Add `--source` to various commands Signed-off-by: Dimitris Karakasilis --- internal/agent/install.go | 39 ++++++++++++---- internal/agent/interactive_install.go | 7 +-- main.go | 65 ++++++++++++++++++++++----- 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/internal/agent/install.go b/internal/agent/install.go index 673c629..d2c99d0 100644 --- a/internal/agent/install.go +++ b/internal/agent/install.go @@ -53,16 +53,16 @@ func displayInfo(agentConfig *Config) { } } -func ManualInstall(c, device string, reboot, poweroff, strictValidations bool) error { +func ManualInstall(c, sourceImg, device string, reboot, poweroff, strictValidations bool) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - source, err := prepareConfiguration(ctx, c) + ConfigSource, err := prepareConfiguration(ctx, c) if err != nil { return err } - cc, err := config.Scan(collector.Directories(source), collector.MergeBootLine, collector.StrictValidation(strictValidations), collector.NoLogs) + cc, err := config.Scan(collector.Directories(ConfigSource), collector.MergeBootLine, collector.StrictValidation(strictValidations), collector.NoLogs) if err != nil { return err } @@ -80,10 +80,10 @@ func ManualInstall(c, device string, reboot, poweroff, strictValidations bool) e // Override from flags! cc.Install.Device = device } - return RunInstall(cc) + return RunInstall(cc, sourceImg) } -func Install(dir ...string) error { +func Install(sourceImg string, dir ...string) error { var cc *config.Config var err error @@ -120,7 +120,7 @@ func Install(dir ...string) error { // runs the installation cc, err = config.Scan(collector.Directories(dir...), collector.MergeBootLine) if err == nil && cc.Install != nil && cc.Install.Auto { - err = RunInstall(cc) + err = RunInstall(cc, sourceImg) if err != nil { return err } @@ -184,7 +184,7 @@ func Install(dir ...string) error { pterm.Info.Println("Starting installation") cc.Logger.Debugf("Runinstall with cc: %s\n", litter.Sdump(cc)) - if err := RunInstall(cc); err != nil { + if err := RunInstall(cc, sourceImg); err != nil { return err } @@ -213,7 +213,7 @@ func Install(dir ...string) error { return nil } -func RunInstall(c *config.Config) error { +func RunInstall(c *config.Config, sourceImg string) error { utils.SetEnv(c.Env) utils.SetEnv(c.Install.Env) @@ -250,7 +250,28 @@ func RunInstall(c *config.Config) error { // 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 != "" { + if sourceImg != "" { + imgSource, err := v1.NewSrcFromURI(sourceImg) + if err != nil { + return err + } + installSpec.Active.Source = imgSource + + // TODO: Why only setting active source above? What about size? + // TODO: These 2 blocks are identical, DRY them. + + // size, err := GetSourceSize(cfg, imgSource) + // if err != nil { + // c.Logger.Warnf("Failed to infer size for images: %s", err.Error()) + // } + + // installSpec.Active.Source = imgSource + // installSpec.Passive.Source = imgSource + // installSpec.Recovery.Source = imgSource + // installSpec.Active.Size = uint(size) + // installSpec.Passive.Size = uint(size) + // installSpec.Recovery.Size = uint(size) + } else if c.Install.Image != "" { imgSource, err := v1.NewSrcFromURI(c.Install.Image) if err != nil { return err diff --git a/internal/agent/interactive_install.go b/internal/agent/interactive_install.go index 7279300..8b65b94 100644 --- a/internal/agent/interactive_install.go +++ b/internal/agent/interactive_install.go @@ -129,7 +129,7 @@ func detectDevice() string { return preferedDevice } -func InteractiveInstall(debug, spawnShell bool) error { +func InteractiveInstall(debug, spawnShell bool, sourceImg string) error { var sshUsers []string bus.Manager.Initialize() @@ -229,7 +229,7 @@ func InteractiveInstall(debug, spawnShell bool) error { } if !isYes(allGood) { - return InteractiveInstall(debug, spawnShell) + return InteractiveInstall(debug, spawnShell, sourceImg) } usersToSet := map[string]schema.User{} @@ -283,6 +283,7 @@ func InteractiveInstall(debug, spawnShell bool) error { fmt.Printf("could not write event cloud init: %s\n", err.Error()) } // override cc with our new config object from the scan, so it's updated for the RunInstall function + // TODO: Alternative solution: pass a reader here (the new feature) and add the image source cc, _ = config.Scan(collector.Directories(tmpdir), collector.MergeBootLine, collector.NoLogs) } @@ -291,7 +292,7 @@ func InteractiveInstall(debug, spawnShell bool) error { ccString, _ := cc.String() pterm.Info.Println(ccString) - err = RunInstall(cc) + err = RunInstall(cc, sourceImg) if err != nil { pterm.Error.Println(err.Error()) } diff --git a/main.go b/main.go index 3d34a36..0649b75 100644 --- a/main.go +++ b/main.go @@ -110,15 +110,8 @@ See https://kairos.io/docs/upgrade/manual/ for documentation. }, }, Before: func(c *cli.Context) error { - source := c.String("source") - if source != "" { - r, err := regexp.Compile(`^oci:|dir:|file:`) - if err != nil { - return nil - } - if !r.MatchString(source) { - return fmt.Errorf("source %s does not match any of oci:, dir: or file: ", source) - } + if err := validateSource(c.String("source")); err != nil { + return err } return checkRoot() @@ -385,13 +378,23 @@ This command is meant to be used from the boot GRUB menu, but can be also starte &cli.BoolFlag{ Name: "shell", }, + &cli.StringFlag{ + Name: "source", + Usage: "Source for upgrade. 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`", + }, }, Usage: "Starts interactive installation", Before: func(c *cli.Context) error { + if err := validateSource(c.String("source")); err != nil { + return err + } + return checkRoot() }, Action: func(c *cli.Context) error { - return agent.InteractiveInstall(c.Bool("debug"), c.Bool("shell")) + source := c.String("source") + + return agent.InteractiveInstall(c.Bool("debug"), c.Bool("shell"), source) }, }, { @@ -410,8 +413,16 @@ This command is meant to be used from the boot GRUB menu, but can be also starte &cli.BoolFlag{ Name: "reboot", }, + &cli.StringFlag{ + Name: "source", + Usage: "Source for upgrade. 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`", + }, }, Before: func(c *cli.Context) error { + if err := validateSource(c.String("source")); err != nil { + return err + } + return checkRoot() }, Action: func(c *cli.Context) error { @@ -420,7 +431,9 @@ This command is meant to be used from the boot GRUB menu, but can be also starte } config := c.Args().First() - return agent.ManualInstall(config, c.String("device"), c.Bool("reboot"), c.Bool("poweroff"), c.Bool("strict-validation")) + source := c.String("source") + + return agent.ManualInstall(config, source, c.String("device"), c.Bool("reboot"), c.Bool("poweroff"), c.Bool("strict-validation")) }, }, { @@ -436,10 +449,22 @@ See also https://kairos.io/docs/installation/qrcode/ for documentation. This command is meant to be used from the boot GRUB menu, but can be started manually`, Aliases: []string{"i"}, Before: func(c *cli.Context) error { + if err := validateSource(c.String("source")); err != nil { + return err + } + return checkRoot() }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "source", + Usage: "Source for upgrade. 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) error { - return agent.Install(configScanDir...) + source := c.String("source") + + return agent.Install(source, configScanDir...) }, }, { @@ -686,3 +711,19 @@ func checkRoot() error { return nil } + +func validateSource(source string) error { + if source == "" { + return nil + } + + r, err := regexp.Compile(`^oci:|dir:|file:`) + if err != nil { + return err + } + if !r.MatchString(source) { + return fmt.Errorf("source %s does not match any of oci:, dir: or file: ", source) + } + + return nil +}