1
0
mirror of https://github.com/rancher/os.git synced 2025-09-07 01:31:06 +00:00
Files
os/pkg/config/read.go
Darren Shepherd b00581c2d7 Various fixes
2021-10-22 23:22:09 -07:00

204 lines
4.6 KiB
Go

package config
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"regexp"
"strings"
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.AddMapper("config", defaultMappers)
s.AddMapper("rancherOS", defaultMappers)
s.AddMapper("install", defaultMappers)
return s
}).MustImport(Config{})
schema = schemas.Schema("config")
)
func ToEnv(cfg Config) ([]string, error) {
data, err := convert.EncodeToMap(&cfg)
if err != nil {
return nil, err
}
return mapToEnv("", data), nil
}
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, "RANCHEROS_", "COS_")
if data, ok := v.(map[string]interface{}); ok {
subResult := mapToEnv(keyName+"_", data)
result = append(result, subResult...)
} else {
result = append(result, fmt.Sprintf("%s=%v", keyName, v))
}
}
return result
}
func readFileFunc(path string) func() (map[string]interface{}, error) {
return func() (map[string]interface{}, error) {
return readFile(path)
}
}
func readNested(data map[string]interface{}) (map[string]interface{}, error) {
var (
nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "rancheros", "install", "configUrl"))
funcs []reader
)
if len(nestedConfigFiles) == 0 {
return data, nil
}
values.RemoveValue(data, "rancheros", "install", "configUrl")
for _, nestedConfigFile := range nestedConfigFiles {
funcs = append(funcs, readFileFunc(nestedConfigFile))
}
funcs = append(funcs, func() (map[string]interface{}, error) {
return data, nil
})
return merge(funcs...)
}
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")
}
}()
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)
}
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
}
newData, err = readNested(newData)
if err != nil {
return nil, err
}
d = values.MergeMapsConcatSlice(d, newData)
}
return d, nil
}
func readConfigMap(cfg string) (map[string]interface{}, error) {
return merge(readCmdline, readFileFunc(cfg))
}
func ReadConfig(cfg string) (result Config, err error) {
data, err := readConfigMap(cfg)
if err != nil {
return result, err
}
return result, convert.ToObj(data, &result)
}
func readCmdline() (map[string]interface{}, error) {
//supporting regex https://regexr.com/4mq0s
parser, err := regexp.Compile(`(\"[^\"]+\")|([^\s]+=(\"[^\"]+\")|([^\s]+))`)
if err != nil {
return nil, nil
}
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 {
return nil, err
}
data := map[string]interface{}{}
for _, item := range parser.FindAllString(string(bytes), -1) {
parts := strings.SplitN(item, "=", 2)
value := "true"
if len(parts) > 1 {
value = strings.Trim(parts[1], `"`)
}
keys := strings.Split(strings.Trim(parts[0], `"`), ".")
existing, ok := values.GetValue(data, keys...)
if ok {
switch v := existing.(type) {
case string:
values.PutValue(data, []string{v, value}, keys...)
case []string:
values.PutValue(data, append(v, value), keys...)
}
} else {
values.PutValue(data, value, keys...)
}
}
return data, nil
}