mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-09-20 01:42:20 +00:00
This changeset also adds a `config_url` and `options` keyword in the c3os config. Along with that the config logic is changed so the configuration is taken also from boot commands and merged in the final installed config file.
193 lines
4.2 KiB
Go
193 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"time"
|
|
|
|
events "github.com/c3os-io/c3os/pkg/bus"
|
|
config "github.com/c3os-io/c3os/pkg/config"
|
|
|
|
"github.com/c3os-io/c3os/internal/bus"
|
|
"github.com/c3os-io/c3os/internal/c3os"
|
|
"github.com/c3os-io/c3os/internal/cmd"
|
|
"github.com/c3os-io/c3os/internal/utils"
|
|
|
|
machine "github.com/c3os-io/c3os/internal/machine"
|
|
qr "github.com/mudler/go-nodepair/qrcode"
|
|
"github.com/mudler/go-pluggable"
|
|
"github.com/pterm/pterm"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
func optsToArgs(options map[string]string) (res []string) {
|
|
for k, v := range options {
|
|
if k != "device" && k != "cc" && k != "reboot" && k != "poweroff" {
|
|
res = append(res, fmt.Sprintf("--%s", k))
|
|
res = append(res, fmt.Sprintf("%s", v))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func install(dir ...string) error {
|
|
utils.OnSignal(func() {
|
|
svc, err := machine.Getty(1)
|
|
if err == nil {
|
|
svc.Start()
|
|
}
|
|
}, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
tk := ""
|
|
r := map[string]string{}
|
|
|
|
mergeOption := func(cloudConfig string) {
|
|
c := &config.Config{}
|
|
yaml.Unmarshal([]byte(cloudConfig), c)
|
|
for k, v := range c.Options {
|
|
if k == "cc" {
|
|
continue
|
|
}
|
|
r[k] = v
|
|
}
|
|
}
|
|
bus.Manager.Response(events.EventChallenge, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
|
tk = r.Data
|
|
})
|
|
bus.Manager.Response(events.EventInstall, func(p *pluggable.Plugin, resp *pluggable.EventResponse) {
|
|
err := json.Unmarshal([]byte(resp.Data), &r)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
})
|
|
|
|
// Reads config, and if present and offline is defined,
|
|
// runs the installation
|
|
cc, err := config.Scan(config.Directories(dir...), config.MergeBootLine)
|
|
if err == nil && cc.C3OS != nil && cc.C3OS.Offline {
|
|
r["cc"] = cc.String()
|
|
r["device"] = cc.C3OS.Device
|
|
mergeOption(cc.String())
|
|
|
|
runInstall(r)
|
|
|
|
svc, err := machine.Getty(1)
|
|
if err == nil {
|
|
svc.Start()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
_, err = bus.Manager.Publish(events.EventChallenge, events.EventPayload{Config: cc.String()})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd.PrintBranding(banner)
|
|
|
|
cmd.PrintTextFromFile(c3os.BrandingFile("install_text"), "Installation")
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
if tk != "" {
|
|
qr.Print(tk)
|
|
}
|
|
|
|
if _, err := bus.Manager.Publish(events.EventInstall, events.InstallPayload{Token: tk, Config: cc.String()}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(r) == 0 {
|
|
return errors.New("no configuration, stopping installation")
|
|
}
|
|
|
|
cloudConfig, exists := r["cc"]
|
|
mergeOption(cloudConfig)
|
|
|
|
ccData := map[string]interface{}{}
|
|
yaml.Unmarshal([]byte(cc.String()), &ccData)
|
|
if exists {
|
|
yaml.Unmarshal([]byte(cloudConfig), &ccData)
|
|
}
|
|
|
|
out, err := yaml.Marshal(ccData)
|
|
if err != nil {
|
|
return fmt.Errorf("failed marshalling cc: %w", err)
|
|
}
|
|
|
|
r["cc"] = string(out)
|
|
|
|
pterm.Info.Println("Starting installation")
|
|
utils.SH("elemental run-stage c3os-install.pre")
|
|
bus.RunHookScript("/usr/bin/c3os-agent.install.pre.hook")
|
|
|
|
runInstall(r)
|
|
|
|
pterm.Info.Println("Installation completed, press enter to go back to the shell.")
|
|
|
|
utils.Prompt("")
|
|
|
|
// give tty1 back
|
|
svc, err := machine.Getty(1)
|
|
if err == nil {
|
|
svc.Start()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runInstall(options map[string]string) error {
|
|
f, _ := ioutil.TempFile("", "xxxx")
|
|
defer os.RemoveAll(f.Name())
|
|
|
|
device, ok := options["device"]
|
|
if !ok {
|
|
fmt.Println("device must be specified among options")
|
|
os.Exit(1)
|
|
}
|
|
|
|
cloudInit, ok := options["cc"]
|
|
if !ok {
|
|
fmt.Println("cloudInit must be specified among options")
|
|
os.Exit(1)
|
|
}
|
|
|
|
c := &config.Config{}
|
|
yaml.Unmarshal([]byte(cloudInit), c)
|
|
|
|
_, reboot := options["reboot"]
|
|
_, poweroff := options["poweroff"]
|
|
|
|
ioutil.WriteFile(f.Name(), []byte(cloudInit), os.ModePerm)
|
|
args := []string{"install"}
|
|
args = append(args, optsToArgs(options)...)
|
|
args = append(args, "-c", f.Name(), fmt.Sprintf("%s", device))
|
|
|
|
cmd := exec.Command("elemental", args...)
|
|
cmd.Env = os.Environ()
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
utils.SH("elemental run-stage c3os-install.after")
|
|
bus.RunHookScript("/usr/bin/c3os-agent.install.after.hook")
|
|
|
|
if reboot || c.C3OS != nil && c.C3OS.Reboot {
|
|
utils.Reboot()
|
|
}
|
|
|
|
if poweroff || c.C3OS != nil && c.C3OS.Poweroff {
|
|
utils.PowerOFF()
|
|
}
|
|
return nil
|
|
}
|