mirror of
https://github.com/rancher/os.git
synced 2025-09-25 12:47:20 +00:00
Add ipxe support
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package config
|
||||
|
||||
type Rancher struct {
|
||||
type RancherOS struct {
|
||||
Install Install `json:"install,omitempty"`
|
||||
}
|
||||
|
||||
@@ -15,31 +15,22 @@ type Install struct {
|
||||
NoFormat bool `json:"noFormat,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
TTY string `json:"tty,omitempty"`
|
||||
ServerURL string `json:"serverUrl,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
|
||||
Rancher Rancher `json:"rancher,omitempty"`
|
||||
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
|
||||
RancherOS RancherOS `json:"rancheros,omitempty"`
|
||||
}
|
||||
|
||||
type YipConfig struct {
|
||||
Stages map[string][]Stage `json:"stages,omitempty"`
|
||||
Rancherd Rancherd `json:"rancherd,omitempty"`
|
||||
Stages map[string][]Stage `json:"stages,omitempty"`
|
||||
}
|
||||
|
||||
type Stage struct {
|
||||
Users map[string]User `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
type Rancherd struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
PasswordHash string `json:"passwd,omitempty"`
|
||||
|
@@ -3,6 +3,7 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -10,18 +11,20 @@ import (
|
||||
values "github.com/rancher/wrangler/pkg/data"
|
||||
"github.com/rancher/wrangler/pkg/data/convert"
|
||||
schemas2 "github.com/rancher/wrangler/pkg/schemas"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultMappers = schemas2.Mappers{
|
||||
NewToMap(),
|
||||
NewToSlice(),
|
||||
NewToBool(),
|
||||
&FuzzyNames{},
|
||||
}
|
||||
schemas = schemas2.EmptySchemas().Init(func(s *schemas2.Schemas) *schemas2.Schemas {
|
||||
s.DefaultMapper = func() schemas2.Mapper {
|
||||
return schemas2.Mappers{
|
||||
NewToMap(),
|
||||
NewToSlice(),
|
||||
NewToBool(),
|
||||
&FuzzyNames{},
|
||||
}
|
||||
}
|
||||
s.AddMapper("config", defaultMappers)
|
||||
s.AddMapper("rancherOS", defaultMappers)
|
||||
s.AddMapper("install", defaultMappers)
|
||||
return s
|
||||
}).MustImport(Config{})
|
||||
schema = schemas.Schema("config")
|
||||
@@ -40,7 +43,7 @@ func mapToEnv(prefix string, data map[string]interface{}) []string {
|
||||
var result []string
|
||||
for k, v := range data {
|
||||
keyName := strings.ToUpper(prefix + convert.ToYAMLKey(k))
|
||||
keyName = strings.ReplaceAll(keyName, "RANCHER_", "COS_")
|
||||
keyName = strings.ReplaceAll(keyName, "RANCHEROS_", "COS_")
|
||||
if data, ok := v.(map[string]interface{}); ok {
|
||||
subResult := mapToEnv(keyName+"_", data)
|
||||
result = append(result, subResult...)
|
||||
@@ -51,14 +54,86 @@ func mapToEnv(prefix string, data map[string]interface{}) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func ReadConfig() (Config, error) {
|
||||
result := Config{}
|
||||
func readFile(path string) (result map[string]interface{}, _ error) {
|
||||
result = map[string]interface{}{}
|
||||
defer func() {
|
||||
if v, ok := result["install"]; ok {
|
||||
values.PutValue(result, v, "rancheros", "install")
|
||||
}
|
||||
}()
|
||||
|
||||
data, err := readCmdline()
|
||||
if err != nil {
|
||||
return result, err
|
||||
switch {
|
||||
case strings.HasPrefix(path, "http://"):
|
||||
fallthrough
|
||||
case strings.HasPrefix(path, "https://"):
|
||||
resp, err := http.Get(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
buffer, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
|
||||
return result, yaml.Unmarshal(buffer, &result)
|
||||
case strings.HasPrefix(path, "tftp://"):
|
||||
return tftpGet(path)
|
||||
}
|
||||
if err := schema.Mapper.ToInternal(data); err != nil {
|
||||
|
||||
f, err := ioutil.ReadFile(path)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{}
|
||||
if err := yaml.Unmarshal(f, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
type reader func() (map[string]interface{}, error)
|
||||
|
||||
func merge(readers ...reader) (map[string]interface{}, error) {
|
||||
d := map[string]interface{}{}
|
||||
for _, r := range readers {
|
||||
newData, err := r()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := schema.Mapper.ToInternal(newData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d = values.MergeMapsConcatSlice(d, newData)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func readConfigMap(cfg string) (map[string]interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
data map[string]interface{}
|
||||
)
|
||||
return merge(func() (map[string]interface{}, error) {
|
||||
data, err = merge(readCmdline,
|
||||
func() (map[string]interface{}, error) {
|
||||
return readFile(cfg)
|
||||
},
|
||||
)
|
||||
return data, err
|
||||
}, func() (map[string]interface{}, error) {
|
||||
return readFile(convert.ToString(values.GetValueN(data, "rancheros", "install", "configUrl")))
|
||||
})
|
||||
}
|
||||
|
||||
func ReadConfig(cfg string) (result Config, err error) {
|
||||
data, err := readConfigMap(cfg)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -72,7 +147,11 @@ func readCmdline() (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile("/proc/cmdline")
|
||||
procCmdLine := os.Getenv("PROC_CMDLINE")
|
||||
if procCmdLine == "" {
|
||||
procCmdLine = "/proc/cmdline"
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(procCmdLine)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
|
@@ -30,7 +30,9 @@ func (f *FuzzyNames) addName(name, toName string) {
|
||||
}
|
||||
|
||||
func (f *FuzzyNames) ModifySchema(schema *schemas2.Schema, schemas *schemas2.Schemas) error {
|
||||
f.names = map[string]string{}
|
||||
if f.names == nil {
|
||||
f.names = map[string]string{}
|
||||
}
|
||||
|
||||
for name := range schema.ResourceFields {
|
||||
if strings.HasSuffix(name, "s") && len(name) > 1 {
|
||||
|
41
pkg/config/tftpget.go
Normal file
41
pkg/config/tftpget.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"gopkg.in/pin/tftp.v2"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func tftpGet(tftpURL string) (map[string]interface{}, error) {
|
||||
u, err := url.Parse(tftpURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
host = u.Host + ":69"
|
||||
}
|
||||
|
||||
fmt.Printf("Downloading config from host %s, file %s\n", host, u.Path)
|
||||
client, err := tftp.NewClient(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writerTo, err := client.Receive(u.Path, "octet")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if _, err := writerTo.WriteTo(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[string]interface{}{}
|
||||
return result, yaml.Unmarshal(buf.Bytes(), &result)
|
||||
}
|
@@ -6,10 +6,10 @@ import (
|
||||
)
|
||||
|
||||
func PrintInstall(cfg Config) ([]byte, error) {
|
||||
if cfg.Rancher.Install.Password != "" {
|
||||
cfg.Rancher.Install.Password = "******"
|
||||
if cfg.RancherOS.Install.Password != "" {
|
||||
cfg.RancherOS.Install.Password = "******"
|
||||
}
|
||||
data, err := convert.EncodeToMap(cfg.Rancher.Install)
|
||||
data, err := convert.EncodeToMap(cfg.RancherOS.Install)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -10,10 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func Ask(cfg *config.Config) error {
|
||||
if cfg.Rancher.Install.Silent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := AskInstallDevice(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -22,7 +18,7 @@ func Ask(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.Rancher.Install.ConfigURL == "" {
|
||||
if cfg.RancherOS.Install.ConfigURL == "" {
|
||||
if err := AskGithub(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,17 +26,13 @@ func Ask(cfg *config.Config) error {
|
||||
if err := AskPassword(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AskServerAgent(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AskInstallDevice(cfg *config.Config) error {
|
||||
if cfg.Rancher.Install.Device != "" {
|
||||
if cfg.RancherOS.Install.Device != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,69 +46,22 @@ func AskInstallDevice(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Rancher.Install.Device = "/dev/" + fields[i]
|
||||
cfg.RancherOS.Install.Device = "/dev/" + fields[i]
|
||||
return nil
|
||||
}
|
||||
|
||||
func AskToken(cfg *config.Config, server bool) error {
|
||||
var (
|
||||
token string
|
||||
err error
|
||||
)
|
||||
|
||||
if cfg.Rancher.Install.Token != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := "Token or cluster secret"
|
||||
if server {
|
||||
msg += " (optional)"
|
||||
}
|
||||
if server {
|
||||
token, err = questions.PromptOptional(msg+": ", "")
|
||||
} else {
|
||||
token, err = questions.Prompt(msg+": ", "")
|
||||
}
|
||||
cfg.Rancher.Install.Token = token
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func isServer(cfg *config.Config) (bool, error) {
|
||||
opts := []string{"server", "agent"}
|
||||
func isServer(cfg *config.Config) (bool, bool, error) {
|
||||
opts := []string{"server", "agent", "none"}
|
||||
i, err := questions.PromptFormattedOptions("Run as server or agent?", 0, opts...)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
return i == 0, nil
|
||||
}
|
||||
|
||||
func AskServerAgent(cfg *config.Config) error {
|
||||
if cfg.Rancher.Install.ServerURL != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
server, err := isServer(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if server {
|
||||
return AskToken(cfg, true)
|
||||
}
|
||||
|
||||
url, err := questions.Prompt("URL of server: ", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Rancher.Install.ServerURL = url
|
||||
|
||||
return AskToken(cfg, false)
|
||||
return i == 0, i == 1, nil
|
||||
}
|
||||
|
||||
func AskPassword(cfg *config.Config) error {
|
||||
if cfg.Rancher.Install.Silent || cfg.Rancher.Install.Password != "" {
|
||||
if cfg.RancherOS.Install.Silent || cfg.RancherOS.Install.Password != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -140,12 +85,12 @@ func AskPassword(cfg *config.Config) error {
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Rancher.Install.Password = pass
|
||||
cfg.RancherOS.Install.Password = pass
|
||||
return nil
|
||||
}
|
||||
|
||||
func AskGithub(cfg *config.Config) error {
|
||||
if len(cfg.SSHAuthorizedKeys) > 0 || cfg.Rancher.Install.Password != "" {
|
||||
if len(cfg.SSHAuthorizedKeys) > 0 || cfg.RancherOS.Install.Password != "" || cfg.RancherOS.Install.Silent {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -167,7 +112,7 @@ func AskGithub(cfg *config.Config) error {
|
||||
}
|
||||
|
||||
func AskConfigURL(cfg *config.Config) error {
|
||||
if cfg.Rancher.Install.ConfigURL != "" {
|
||||
if cfg.RancherOS.Install.ConfigURL != "" || cfg.RancherOS.Install.Silent {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,6 +130,6 @@ func AskConfigURL(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Rancher.Install.ConfigURL = str
|
||||
cfg.RancherOS.Install.ConfigURL = str
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -10,16 +11,16 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func Run(automatic bool) error {
|
||||
cfg, err := config.ReadConfig()
|
||||
func Run(automatic bool, configFile string) error {
|
||||
cfg, err := config.ReadConfig(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if automatic && !cfg.Rancher.Install.Automatic {
|
||||
if automatic && !cfg.RancherOS.Install.Automatic {
|
||||
return nil
|
||||
} else if automatic {
|
||||
cfg.Rancher.Install.Silent = true
|
||||
cfg.RancherOS.Install.Silent = true
|
||||
}
|
||||
|
||||
err = Ask(&cfg)
|
||||
@@ -44,7 +45,7 @@ func runInstall(cfg config.Config, output string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !cfg.Rancher.Install.Silent {
|
||||
if !cfg.RancherOS.Install.Silent {
|
||||
val, err := questions.PromptBool("\nConfiguration\n"+"-------------\n\n"+
|
||||
string(installBytes)+
|
||||
"\nYour disk will be formatted and installed with the above configuration.\nContinue?", false)
|
||||
@@ -53,30 +54,20 @@ func runInstall(cfg config.Config, output string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Rancher.Install.ConfigURL == "" {
|
||||
yip := config.YipConfig{
|
||||
Rancherd: config.Rancherd{
|
||||
Server: cfg.Rancher.Install.ServerURL,
|
||||
Token: cfg.Rancher.Install.Token,
|
||||
},
|
||||
}
|
||||
if cfg.Rancher.Install.ServerURL == "" {
|
||||
yip.Rancherd.Role = "cluster-init"
|
||||
} else {
|
||||
yip.Rancherd.Role = "agent"
|
||||
}
|
||||
if cfg.Rancher.Install.Password != "" || len(cfg.SSHAuthorizedKeys) > 0 {
|
||||
if cfg.RancherOS.Install.ConfigURL == "" && !cfg.RancherOS.Install.Silent {
|
||||
yip := config.YipConfig{}
|
||||
if cfg.RancherOS.Install.Password != "" || len(cfg.SSHAuthorizedKeys) > 0 {
|
||||
yip.Stages = map[string][]config.Stage{
|
||||
"network": {{
|
||||
Users: map[string]config.User{
|
||||
"root": {
|
||||
Name: "root",
|
||||
PasswordHash: cfg.Rancher.Install.Password,
|
||||
PasswordHash: cfg.RancherOS.Install.Password,
|
||||
SSHAuthorizedKeys: cfg.SSHAuthorizedKeys,
|
||||
},
|
||||
}},
|
||||
}}
|
||||
cfg.Rancher.Install.Password = ""
|
||||
cfg.RancherOS.Install.Password = ""
|
||||
}
|
||||
|
||||
data, err := yaml.Marshal(yip)
|
||||
@@ -87,7 +78,7 @@ func runInstall(cfg config.Config, output string) error {
|
||||
if err := ioutil.WriteFile(output+".yip", data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.Rancher.Install.ConfigURL = output + ".yip"
|
||||
cfg.RancherOS.Install.ConfigURL = output + ".yip"
|
||||
}
|
||||
|
||||
ev, err := config.ToEnv(cfg)
|
||||
@@ -95,6 +86,8 @@ func runInstall(cfg config.Config, output string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
printEnv(cfg)
|
||||
|
||||
cmd := exec.Command("cos-installer")
|
||||
cmd.Env = append(os.Environ(), ev...)
|
||||
cmd.Stdout = os.Stdout
|
||||
@@ -102,3 +95,19 @@ func runInstall(cfg config.Config, output string) error {
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func printEnv(cfg config.Config) {
|
||||
if cfg.RancherOS.Install.Password != "" {
|
||||
cfg.RancherOS.Install.Password = "<removed>"
|
||||
}
|
||||
|
||||
ev2, err := config.ToEnv(cfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Install environment:")
|
||||
for _, ev := range ev2 {
|
||||
fmt.Println(ev)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user