kairos-agent/internal/agent/interactive_install.go

196 lines
4.3 KiB
Go

package agent
import (
"fmt"
"strings"
"github.com/c3os-io/c3os/internal/cmd"
providerConfig "github.com/c3os-io/c3os/internal/provider/config"
config "github.com/c3os-io/c3os/pkg/config"
"github.com/c3os-io/c3os/pkg/utils"
"github.com/erikgeiser/promptkit/textinput"
"github.com/jaypipes/ghw"
"github.com/mudler/edgevpn/pkg/node"
"github.com/mudler/yip/pkg/schema"
"github.com/pterm/pterm"
)
const (
canBeEmpty = "Unset"
yesNo = "[y]es/[N]o"
)
func prompt(prompt, initialValue, placeHolder string, canBeEmpty, hidden bool) (string, error) {
input := textinput.New(prompt)
input.InitialValue = initialValue
input.Placeholder = placeHolder
if canBeEmpty {
input.Validate = func(s string) bool { return true }
}
input.Hidden = hidden
return input.RunPrompt()
}
func isYes(s string) bool {
i := strings.ToLower(s)
if i == "y" || i == "yes" {
return true
}
return false
}
const (
_ = 1 << (10 * iota)
KiB
MiB
GiB
TiB
)
func InteractiveInstall(spawnShell bool) error {
cmd.PrintBranding(DefaultBanner)
pterm.DefaultBox.WithTitle("Installation").WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println(
`Interactive installation. Documentation is available at https://docs.c3os.io.`)
disks := []string{}
maxSize := float64(0)
preferedDevice := "/dev/sda"
block, err := ghw.Block()
if err == nil {
for _, disk := range block.Disks {
size := float64(disk.SizeBytes) / float64(GiB)
if size > maxSize {
maxSize = size
preferedDevice = "/dev/" + disk.Name
}
disks = append(disks, fmt.Sprintf("/dev/%s: %s (%.2f GiB) ", disk.Name, disk.Model, float64(disk.SizeBytes)/float64(GiB)))
}
}
pterm.Info.Println("Available Disks:")
for _, d := range disks {
pterm.Info.Println(" " + d)
}
var networkToken string
device, err := prompt("What's the target install device?", preferedDevice, "Cannot be empty", false, false)
if err != nil {
return err
}
userName, err := prompt("User to setup", "c3os", canBeEmpty, true, false)
if err != nil {
return err
}
userPassword, err := prompt("Password", "", canBeEmpty, true, true)
if err != nil {
return err
}
if userPassword == "" {
userPassword = "!"
}
users, err := prompt("SSH access (rsakey, github/gitlab supported, comma-separated)", "github:someuser,github:someuser2", canBeEmpty, true, false)
if err != nil {
return err
}
sshUsers := strings.Split(users, ",")
k3sAuto, err := prompt("Do you want to enable k3s automated setup? (requires multiple nodes)", "n", yesNo, true, false)
if err != nil {
return err
}
if isYes(k3sAuto) {
hasNetworkToken, err := prompt("Do you have a network token already?", "n", yesNo, true, false)
if err != nil {
return err
}
if isYes(hasNetworkToken) {
networkToken, err = prompt("Input network token", "", "", false, true)
if err != nil {
return err
}
} else {
networkToken = node.GenerateNewConnectionData().Base64()
}
}
k3sStandalone, err := prompt("Do you want to enable k3s standalone?", "n", yesNo, true, false)
if err != nil {
return err
}
allGood, err := prompt("Are settings ok?", "n", yesNo, true, false)
if err != nil {
return err
}
if !isYes(allGood) {
return InteractiveInstall(spawnShell)
}
c := &config.Config{
Install: &config.Install{
Device: device,
},
}
providerCfg := providerConfig.Config{
C3OS: &providerConfig.C3OS{
NetworkToken: networkToken,
},
K3s: providerConfig.K3s{
Enabled: isYes(k3sStandalone),
},
}
usersToSet := map[string]schema.User{}
if userName != "" {
user := schema.User{
Name: userName,
PasswordHash: userPassword,
Groups: []string{"admin"},
SSHAuthorizedKeys: sshUsers,
}
usersToSet = map[string]schema.User{
userName: user,
}
}
cloudConfig := schema.YipConfig{Name: "Config generated by the installer",
Stages: map[string][]schema.Stage{config.NetworkStage.String(): {
{
Users: usersToSet,
},
}}}
dat, err := config.MergeYAML(cloudConfig, c, providerCfg)
if err != nil {
return err
}
pterm.Info.Println("Starting installation")
err = RunInstall(map[string]string{
"device": device,
"cc": config.AddHeader("#node-config", string(dat)),
})
if err != nil {
pterm.Error.Println(err.Error())
}
if spawnShell {
return utils.Shell().Run()
}
return err
}