mirror of
https://github.com/rancher/os.git
synced 2025-07-01 17:21:50 +00:00
Merge pull request #922 from joshwget/simplify-configuration
Simplify configuration
This commit is contained in:
commit
410dfbe0fd
@ -28,17 +28,6 @@ func configSubcommands() []cli.Command {
|
|||||||
Usage: "set a value",
|
Usage: "set a value",
|
||||||
Action: configSet,
|
Action: configSet,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "import",
|
|
||||||
Usage: "import configuration from standard in or a file",
|
|
||||||
Action: runImport,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "input, i",
|
|
||||||
Usage: "File from which to read",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "images",
|
Name: "images",
|
||||||
Usage: "List Docker images for a configuration from a file",
|
Usage: "List Docker images for a configuration from a file",
|
||||||
@ -142,42 +131,6 @@ func env2map(env []string) map[string]string {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func runImport(c *cli.Context) error {
|
|
||||||
var input io.ReadCloser
|
|
||||||
var err error
|
|
||||||
input = os.Stdin
|
|
||||||
cfg, err := config.LoadConfig()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
inputFile := c.String("input")
|
|
||||||
if inputFile != "" {
|
|
||||||
input, err = os.Open(inputFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer input.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := ioutil.ReadAll(input)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg, err = cfg.Import(bytes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cfg.Save(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func configSet(c *cli.Context) error {
|
func configSet(c *cli.Context) error {
|
||||||
key := c.Args().Get(0)
|
key := c.Args().Get(0)
|
||||||
value := c.Args().Get(1)
|
value := c.Args().Get(1)
|
||||||
@ -185,20 +138,11 @@ func configSet(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadConfig()
|
err := config.Set(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgDiff, err := cfg.Set(key, value)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cfg.Save(cfgDiff); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,14 +152,9 @@ func configGet(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadConfig()
|
val, err := config.Get(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{"err": err}).Fatal("config get: failed to load config")
|
log.WithFields(log.Fields{"key": arg, "val": val, "err": err}).Fatal("config get: failed to retrieve value")
|
||||||
}
|
|
||||||
|
|
||||||
val, err := cfg.GetIgnoreOmitEmpty(arg)
|
|
||||||
if err != nil {
|
|
||||||
log.WithFields(log.Fields{"cfg": cfg, "key": arg, "val": val, "err": err}).Fatal("config get: failed to retrieve value")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printYaml := false
|
printYaml := false
|
||||||
@ -245,25 +184,16 @@ func merge(c *cli.Context) error {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := config.LoadConfig()
|
err = config.Merge(bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err = cfg.MergeBytes(bytes)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cfg.Save(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func export(c *cli.Context) error {
|
func export(c *cli.Context) error {
|
||||||
content, err := config.Dump(c.Bool("private"), c.Bool("full"))
|
content, err := config.Export(c.Bool("private"), c.Bool("full"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -205,13 +205,7 @@ func startUpgradeContainer(image string, stage, force, reboot, kexec bool, upgra
|
|||||||
}
|
}
|
||||||
|
|
||||||
if upgradeConsole {
|
if upgradeConsole {
|
||||||
cfg, err := config.LoadConfig()
|
if err := config.Set("rancher.force_console_rebuild", true); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Rancher.ForceConsoleRebuild = true
|
|
||||||
if err := cfg.Save(); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,10 @@ func serviceSubCommands() []cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateIncludedServices(cfg *config.CloudConfig) error {
|
||||||
|
return config.Set("rancher.services_include", cfg.Rancher.ServicesInclude)
|
||||||
|
}
|
||||||
|
|
||||||
func disable(c *cli.Context) error {
|
func disable(c *cli.Context) error {
|
||||||
changed := false
|
changed := false
|
||||||
cfg, err := config.LoadConfig()
|
cfg, err := config.LoadConfig()
|
||||||
@ -103,7 +107,7 @@ func disable(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if err = cfg.Save(); err != nil {
|
if err = updateIncludedServices(cfg); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +131,7 @@ func del(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if err = cfg.Save(); err != nil {
|
if err = updateIncludedServices(cfg); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +163,7 @@ func enable(c *cli.Context) error {
|
|||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cfg.Save(); err != nil {
|
if err = updateIncludedServices(cfg); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,19 +65,18 @@ func writeCerts(generateServer bool, hostname []string, cfg *config.CloudConfig,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err = cfg.Merge(map[interface{}]interface{}{
|
// certPath, keyPath are already written to by machineUtil.GenerateCert()
|
||||||
"rancher": map[interface{}]interface{}{
|
if err := config.Set("rancher.docker.server_cert", string(cert)); err != nil {
|
||||||
"docker": map[interface{}]interface{}{
|
|
||||||
"server_cert": string(cert),
|
|
||||||
"server_key": string(key),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := config.Set("rancher.docker.server_key", string(key)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cfg.Save() // certPath, keyPath are already written to by machineUtil.GenerateCert()
|
cfg, err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(certPath, []byte(cfg.Rancher.Docker.ServerCert), 0400); err != nil {
|
if err := ioutil.WriteFile(certPath, []byte(cfg.Rancher.Docker.ServerCert), 0400); err != nil {
|
||||||
@ -88,50 +87,45 @@ func writeCerts(generateServer bool, hostname []string, cfg *config.CloudConfig,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCaCerts(cfg *config.CloudConfig, caCertPath, caKeyPath string) (*config.CloudConfig, error) {
|
func writeCaCerts(cfg *config.CloudConfig, caCertPath, caKeyPath string) error {
|
||||||
if cfg.Rancher.Docker.CACert == "" {
|
if cfg.Rancher.Docker.CACert == "" {
|
||||||
if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, NAME, BITS); err != nil {
|
if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, NAME, BITS); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
caCert, err := ioutil.ReadFile(caCertPath)
|
caCert, err := ioutil.ReadFile(caCertPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
caKey, err := ioutil.ReadFile(caKeyPath)
|
caKey, err := ioutil.ReadFile(caKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err = cfg.Merge(map[interface{}]interface{}{
|
// caCertPath, caKeyPath are already written to by machineUtil.GenerateCACertificate()
|
||||||
"rancher": map[interface{}]interface{}{
|
if err := config.Set("rancher.docker.ca_cert", string(caCert)); err != nil {
|
||||||
"docker": map[interface{}]interface{}{
|
return err
|
||||||
"ca_key": string(caKey),
|
}
|
||||||
"ca_cert": string(caCert),
|
if err := config.Set("rancher.docker.ca_key", string(caKey)); err != nil {
|
||||||
},
|
return err
|
||||||
},
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
cfg, err := config.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.Save(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil // caCertPath, caKeyPath are already written to by machineUtil.GenerateCACertificate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(caCertPath, []byte(cfg.Rancher.Docker.CACert), 0400); err != nil {
|
if err := ioutil.WriteFile(caCertPath, []byte(cfg.Rancher.Docker.CACert), 0400); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile(caKeyPath, []byte(cfg.Rancher.Docker.CAKey), 0400); err != nil {
|
if err := ioutil.WriteFile(caKeyPath, []byte(cfg.Rancher.Docker.CAKey), 0400); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsConfCreate(c *cli.Context) error {
|
func tlsConfCreate(c *cli.Context) error {
|
||||||
@ -152,11 +146,6 @@ func generate(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Generate(generateServer bool, outDir string, hostnames []string) error {
|
func Generate(generateServer bool, outDir string, hostnames []string) error {
|
||||||
cfg, err := config.LoadConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if outDir == "" {
|
if outDir == "" {
|
||||||
if generateServer {
|
if generateServer {
|
||||||
outDir = "/etc/docker/tls"
|
outDir = "/etc/docker/tls"
|
||||||
@ -181,7 +170,11 @@ func Generate(generateServer bool, outDir string, hostnames []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err = writeCaCerts(cfg, caCertPath, caKeyPath)
|
cfg, err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = writeCaCerts(cfg, caCertPath, caKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
119
config/config.go
119
config/config.go
@ -3,28 +3,10 @@ package config
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||||
"github.com/rancher/os/util"
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CloudConfig) Import(bytes []byte) (*CloudConfig, error) {
|
|
||||||
data, err := readConfig(bytes, false, CloudConfigPrivateFile)
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewConfig().Merge(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CloudConfig) MergeBytes(bytes []byte) (*CloudConfig, error) {
|
|
||||||
data, err := readConfig(bytes, false)
|
|
||||||
if err != nil {
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
return c.Merge(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
var keysToStringify = []string{
|
var keysToStringify = []string{
|
||||||
"command",
|
"command",
|
||||||
"dns",
|
"dns",
|
||||||
@ -89,47 +71,36 @@ func StringifyValues(data map[interface{}]interface{}) map[interface{}]interface
|
|||||||
return stringifyValue(data, nil).(map[interface{}]interface{})
|
return stringifyValue(data, nil).(map[interface{}]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CloudConfig) Merge(values map[interface{}]interface{}) (*CloudConfig, error) {
|
func Merge(bytes []byte) error {
|
||||||
d := map[interface{}]interface{}{}
|
data, err := readConfig(bytes, false)
|
||||||
if err := util.Convert(c, &d); err != nil {
|
if err != nil {
|
||||||
return c, err
|
return err
|
||||||
}
|
}
|
||||||
r := util.MapsUnion(d, StringifyValues(values))
|
existing, err := readConfig(nil, false, CloudConfigFile)
|
||||||
t := &CloudConfig{}
|
if err != nil {
|
||||||
if err := util.Convert(r, t); err != nil {
|
return err
|
||||||
return c, err
|
|
||||||
}
|
}
|
||||||
return t, nil
|
return WriteToFile(util.Merge(existing, data), CloudConfigFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dump(private, full bool) (string, error) {
|
func Export(private, full bool) (string, error) {
|
||||||
var cfg *CloudConfig
|
rawCfg, err := LoadRawConfig(full)
|
||||||
var err error
|
if private {
|
||||||
|
rawCfg = filterPrivateKeys(rawCfg)
|
||||||
if full {
|
|
||||||
cfg, err = LoadConfig()
|
|
||||||
} else {
|
|
||||||
files := []string{CloudConfigBootFile, CloudConfigPrivateFile, CloudConfigFile}
|
|
||||||
if !private {
|
|
||||||
files = util.FilterStrings(files, func(x string) bool { return x != CloudConfigPrivateFile })
|
|
||||||
}
|
|
||||||
cfg, err = ChainCfgFuncs(nil,
|
|
||||||
func(_ *CloudConfig) (*CloudConfig, error) { return ReadConfig(nil, true, files...) },
|
|
||||||
amendNils,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
bytes, err := yaml.Marshal(rawCfg)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := yaml.Marshal(*cfg)
|
|
||||||
return string(bytes), err
|
return string(bytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CloudConfig) Get(key string) (interface{}, error) {
|
func Get(key string) (interface{}, error) {
|
||||||
|
cfg, err := LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
data := map[interface{}]interface{}{}
|
data := map[interface{}]interface{}{}
|
||||||
if err := util.Convert(c, &data); err != nil {
|
if err := util.ConvertIgnoreOmitEmpty(cfg, &data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,58 +108,14 @@ func (c *CloudConfig) Get(key string) (interface{}, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CloudConfig) GetIgnoreOmitEmpty(key string) (interface{}, error) {
|
func Set(key string, value interface{}) error {
|
||||||
data := map[interface{}]interface{}{}
|
|
||||||
if err := util.ConvertIgnoreOmitEmpty(c, &data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _ := getOrSetVal(key, data, nil)
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CloudConfig) Set(key string, value interface{}) (map[interface{}]interface{}, error) {
|
|
||||||
data := map[interface{}]interface{}{}
|
data := map[interface{}]interface{}{}
|
||||||
_, data = getOrSetVal(key, data, value)
|
_, data = getOrSetVal(key, data, value)
|
||||||
|
|
||||||
return data, nil
|
existing, err := readConfig(nil, false, CloudConfigFile)
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CloudConfig) Save(cfgDiffs ...map[interface{}]interface{}) error {
|
|
||||||
files := append([]string{OsConfigFile, OemConfigFile}, CloudConfigDirFiles()...)
|
|
||||||
files = util.FilterStrings(files, func(x string) bool { return x != CloudConfigPrivateFile })
|
|
||||||
exCfg, err := ChainCfgFuncs(nil,
|
|
||||||
func(_ *CloudConfig) (*CloudConfig, error) {
|
|
||||||
return ReadConfig(nil, true, files...)
|
|
||||||
},
|
|
||||||
readCmdline,
|
|
||||||
amendNils)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
exCfg = mergeMetadata(exCfg, readMetadata())
|
return WriteToFile(util.Merge(existing, data), CloudConfigFile)
|
||||||
|
|
||||||
exData := map[interface{}]interface{}{}
|
|
||||||
if err := util.Convert(exCfg, &exData); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data := map[interface{}]interface{}{}
|
|
||||||
if err := util.Convert(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data = util.MapsDifference(data, exData)
|
|
||||||
|
|
||||||
// Apply any additional config diffs
|
|
||||||
for _, diff := range cfgDiffs {
|
|
||||||
data = util.MapsUnion(data, diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{"diff": data}).Debug("The diff we're about to save")
|
|
||||||
if err := saveToDisk(data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rancher/os/util"
|
"github.com/rancher/os/util"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFilterKey(t *testing.T) {
|
func TestFilterKey(t *testing.T) {
|
||||||
@ -102,57 +100,6 @@ func TestStringifyValues(t *testing.T) {
|
|||||||
assert.Equal(expected, StringifyValues(data))
|
assert.Equal(expected, StringifyValues(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterDottedKeys(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
|
|
||||||
data := map[interface{}]interface{}{
|
|
||||||
"ssh_authorized_keys": []string{"pubk1", "pubk2"},
|
|
||||||
"hostname": "ros-test",
|
|
||||||
"rancher": map[interface{}]interface{}{
|
|
||||||
"ssh": map[interface{}]interface{}{
|
|
||||||
"keys": map[interface{}]interface{}{
|
|
||||||
"dsa": "dsa-test1",
|
|
||||||
"dsa-pub": "dsa-test2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"docker": map[interface{}]interface{}{
|
|
||||||
"ca_key": "ca_key-test3",
|
|
||||||
"ca_cert": "ca_cert-test4",
|
|
||||||
"args": []string{"args_test5"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
expectedFiltered := map[interface{}]interface{}{
|
|
||||||
"ssh_authorized_keys": []string{"pubk1", "pubk2"},
|
|
||||||
"rancher": map[interface{}]interface{}{
|
|
||||||
"ssh": map[interface{}]interface{}{
|
|
||||||
"keys": map[interface{}]interface{}{
|
|
||||||
"dsa": "dsa-test1",
|
|
||||||
"dsa-pub": "dsa-test2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
expectedRest := map[interface{}]interface{}{
|
|
||||||
"hostname": "ros-test",
|
|
||||||
"rancher": map[interface{}]interface{}{
|
|
||||||
"docker": map[interface{}]interface{}{
|
|
||||||
"ca_key": "ca_key-test3",
|
|
||||||
"ca_cert": "ca_cert-test4",
|
|
||||||
"args": []string{"args_test5"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal([]string{"rancher", "ssh"}, strings.Split("rancher.ssh", "."))
|
|
||||||
assert.Equal([]string{"ssh_authorized_keys"}, strings.Split("ssh_authorized_keys", "."))
|
|
||||||
|
|
||||||
filtered, rest := filterDottedKeys(data, []string{"ssh_authorized_keys", "rancher.ssh"})
|
|
||||||
|
|
||||||
assert.Equal(expectedFiltered, filtered)
|
|
||||||
assert.Equal(expectedRest, rest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalOrReturnString(t *testing.T) {
|
func TestUnmarshalOrReturnString(t *testing.T) {
|
||||||
assert := require.New(t)
|
assert := require.New(t)
|
||||||
|
|
||||||
@ -358,8 +305,6 @@ func TestUserDocker(t *testing.T) {
|
|||||||
err = util.Convert(config, &data)
|
err = util.Convert(config, &data)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
fmt.Println(data)
|
|
||||||
|
|
||||||
val, ok := data["rancher"].(map[interface{}]interface{})["docker"]
|
val, ok := data["rancher"].(map[interface{}]interface{})["docker"]
|
||||||
assert.True(ok)
|
assert.True(ok)
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ import (
|
|||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||||
|
|
||||||
"github.com/rancher/os/util"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CfgFunc func(*CloudConfig) (*CloudConfig, error)
|
type CfgFunc func(*CloudConfig) (*CloudConfig, error)
|
||||||
@ -58,17 +59,12 @@ func filterKey(data map[interface{}]interface{}, key []string) (filtered, rest m
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterDottedKeys(data map[interface{}]interface{}, keys []string) (filtered, rest map[interface{}]interface{}) {
|
func filterPrivateKeys(data map[interface{}]interface{}) map[interface{}]interface{} {
|
||||||
filtered = map[interface{}]interface{}{}
|
for _, privateKey := range PrivateKeys {
|
||||||
rest = util.MapCopy(data)
|
_, data = filterKey(data, strings.Split(privateKey, "."))
|
||||||
|
|
||||||
for _, key := range keys {
|
|
||||||
f, r := filterKey(data, strings.Split(key, "."))
|
|
||||||
filtered = util.MapsUnion(filtered, f)
|
|
||||||
rest = util.MapsIntersection(rest, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
|
func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) (interface{}, map[interface{}]interface{}) {
|
||||||
|
188
config/disk.go
188
config/disk.go
@ -15,14 +15,9 @@ import (
|
|||||||
"github.com/rancher/os/util"
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var osConfig *CloudConfig
|
func NewConfig() map[interface{}]interface{} {
|
||||||
|
osConfig, _ := readConfig(nil, true, OsConfigFile, OemConfigFile)
|
||||||
func NewConfig() *CloudConfig {
|
return osConfig
|
||||||
if osConfig == nil {
|
|
||||||
osConfig, _ = ReadConfig(nil, true, OsConfigFile, OemConfigFile)
|
|
||||||
}
|
|
||||||
newCfg := *osConfig
|
|
||||||
return &newCfg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*CloudConfig, error) {
|
func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*CloudConfig, error) {
|
||||||
@ -39,42 +34,50 @@ func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*Cl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig() (*CloudConfig, error) {
|
func LoadRawConfig(full bool) (map[interface{}]interface{}, error) {
|
||||||
cfg, err := ChainCfgFuncs(NewConfig(),
|
var base map[interface{}]interface{}
|
||||||
readFilesAndMetadata,
|
if full {
|
||||||
readCmdline,
|
base = NewConfig()
|
||||||
amendNils,
|
}
|
||||||
amendContainerNames)
|
user, err := readConfigs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmdline, err := readCmdline()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged := util.Merge(base, util.Merge(user, cmdline))
|
||||||
|
merged, err = applyDebugFlags(merged)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mergeMetadata(merged, readMetadata()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig() (*CloudConfig, error) {
|
||||||
|
rawCfg, err := LoadRawConfig(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{"cfg": cfg, "err": err}).Error("Failed to load config")
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("Merging cloud-config from meta-data and user-data")
|
cfg := &CloudConfig{}
|
||||||
cfg = mergeMetadata(cfg, readMetadata())
|
if err := util.Convert(rawCfg, cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
if cfg.Rancher.Debug {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
if !util.Contains(cfg.Rancher.Docker.Args, "-D") {
|
|
||||||
cfg.Rancher.Docker.Args = append(cfg.Rancher.Docker.Args, "-D")
|
|
||||||
}
|
}
|
||||||
if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
|
cfg, err = amendNils(cfg)
|
||||||
cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
cfg, err = amendContainerNames(cfg)
|
||||||
if util.Contains(cfg.Rancher.Docker.Args, "-D") {
|
if err != nil {
|
||||||
cfg.Rancher.Docker.Args = util.FilterStrings(cfg.Rancher.Docker.Args, func(x string) bool { return x != "-D" })
|
return nil, err
|
||||||
}
|
}
|
||||||
if util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
|
|
||||||
cfg.Rancher.SystemDocker.Args = util.FilterStrings(cfg.Rancher.SystemDocker.Args, func(x string) bool { return x != "-D" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CloudConfigDirFiles() []string {
|
func CloudConfigDirFiles() []string {
|
||||||
files, err := util.DirLs(CloudConfigDir)
|
files, err := ioutil.ReadDir(CloudConfigDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
// do nothing
|
// do nothing
|
||||||
@ -85,36 +88,56 @@ func CloudConfigDirFiles() []string {
|
|||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
files = util.Filter(files, func(x interface{}) bool {
|
var finalFiles []string
|
||||||
f := x.(os.FileInfo)
|
for _, file := range files {
|
||||||
if f.IsDir() || strings.HasPrefix(f.Name(), ".") {
|
if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
|
||||||
return false
|
finalFiles = append(finalFiles, path.Join(CloudConfigDir, file.Name()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return util.ToStrings(util.Map(files, func(x interface{}) interface{} {
|
return finalFiles
|
||||||
return path.Join(CloudConfigDir, x.(os.FileInfo).Name())
|
}
|
||||||
}))
|
|
||||||
|
func applyDebugFlags(rawCfg map[interface{}]interface{}) (map[interface{}]interface{}, error) {
|
||||||
|
cfg := &CloudConfig{}
|
||||||
|
if err := util.Convert(rawCfg, cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Rancher.Debug {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
if !util.Contains(cfg.Rancher.Docker.Args, "-D") {
|
||||||
|
cfg.Rancher.Docker.Args = append(cfg.Rancher.Docker.Args, "-D")
|
||||||
|
}
|
||||||
|
if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") {
|
||||||
|
cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, rawCfg = getOrSetVal("rancher.docker.args", rawCfg, cfg.Rancher.Docker.Args)
|
||||||
|
_, rawCfg = getOrSetVal("rancher.system_docker.args", rawCfg, cfg.Rancher.SystemDocker.Args)
|
||||||
|
return rawCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeMetadata merges certain options from md (meta-data from the datasource)
|
// mergeMetadata merges certain options from md (meta-data from the datasource)
|
||||||
// onto cc (a CloudConfig derived from user-data), if they are not already set
|
// onto cc (a CloudConfig derived from user-data), if they are not already set
|
||||||
// on cc (i.e. user-data always takes precedence)
|
// on cc (i.e. user-data always takes precedence)
|
||||||
func mergeMetadata(cc *CloudConfig, md datasource.Metadata) *CloudConfig {
|
func mergeMetadata(rawCfg map[interface{}]interface{}, md datasource.Metadata) map[interface{}]interface{} {
|
||||||
if cc == nil {
|
if rawCfg == nil {
|
||||||
return cc
|
return nil
|
||||||
|
}
|
||||||
|
out := util.MapCopy(rawCfg)
|
||||||
|
|
||||||
|
outHostname, ok := out["hostname"]
|
||||||
|
if !ok {
|
||||||
|
outHostname = ""
|
||||||
}
|
}
|
||||||
out := cc
|
|
||||||
dirty := false
|
|
||||||
|
|
||||||
if md.Hostname != "" {
|
if md.Hostname != "" {
|
||||||
if out.Hostname != "" {
|
if outHostname != "" {
|
||||||
log.Debugf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname)
|
log.Debugf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", outHostname, md.Hostname)
|
||||||
} else {
|
} else {
|
||||||
out = &(*cc)
|
out["hostname"] = md.Hostname
|
||||||
dirty = true
|
|
||||||
out.Hostname = md.Hostname
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,14 +149,18 @@ func mergeMetadata(cc *CloudConfig, md datasource.Metadata) *CloudConfig {
|
|||||||
|
|
||||||
sort.Sort(sort.StringSlice(keys))
|
sort.Sort(sort.StringSlice(keys))
|
||||||
|
|
||||||
|
currentKeys, ok := out["ssh_authorized_keys"]
|
||||||
|
if !ok {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
finalKeys := currentKeys.([]interface{})
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if !dirty {
|
finalKeys = append(finalKeys, md.SSHPublicKeys[k])
|
||||||
out = &(*cc)
|
|
||||||
dirty = true
|
|
||||||
}
|
|
||||||
out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, md.SSHPublicKeys[k])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out["ssh_authorized_keys"] = finalKeys
|
||||||
|
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,44 +172,32 @@ func readMetadata() datasource.Metadata {
|
|||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFilesAndMetadata(c *CloudConfig) (*CloudConfig, error) {
|
func readConfigs() (map[interface{}]interface{}, error) {
|
||||||
files := append(CloudConfigDirFiles(), CloudConfigFile)
|
files := append(CloudConfigDirFiles(), CloudConfigFile)
|
||||||
data, err := readConfig(nil, true, files...)
|
data, err := readConfig(nil, true, files...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{"err": err, "files": files}).Error("Error reading config files")
|
return nil, err
|
||||||
return c, err
|
|
||||||
}
|
}
|
||||||
|
return data, nil
|
||||||
t, err := c.Merge(data)
|
|
||||||
if err != nil {
|
|
||||||
log.WithFields(log.Fields{"cfg": c, "data": data, "err": err}).Error("Error merging config data")
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readCmdline(c *CloudConfig) (*CloudConfig, error) {
|
func readCmdline() (map[interface{}]interface{}, error) {
|
||||||
log.Debug("Reading config cmdline")
|
log.Debug("Reading config cmdline")
|
||||||
cmdLine, err := ioutil.ReadFile("/proc/cmdline")
|
cmdLine, err := ioutil.ReadFile("/proc/cmdline")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{"err": err}).Error("Failed to read kernel params")
|
log.WithFields(log.Fields{"err": err}).Error("Failed to read kernel params")
|
||||||
return c, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cmdLine) == 0 {
|
if len(cmdLine) == 0 {
|
||||||
return c, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Config cmdline %s", cmdLine)
|
log.Debugf("Config cmdline %s", cmdLine)
|
||||||
|
|
||||||
cmdLineObj := parseCmdline(strings.TrimSpace(string(cmdLine)))
|
cmdLineObj := parseCmdline(strings.TrimSpace(string(cmdLine)))
|
||||||
|
|
||||||
t, err := c.Merge(cmdLineObj)
|
return cmdLineObj, nil
|
||||||
if err != nil {
|
|
||||||
log.WithFields(log.Fields{"cfg": c, "cmdLine": cmdLine, "data": cmdLineObj, "err": err}).Warn("Error adding kernel params to config")
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func amendNils(c *CloudConfig) (*CloudConfig, error) {
|
func amendNils(c *CloudConfig) (*CloudConfig, error) {
|
||||||
@ -227,23 +242,6 @@ func WriteToFile(data interface{}, filename string) error {
|
|||||||
return ioutil.WriteFile(filename, content, 400)
|
return ioutil.WriteFile(filename, content, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveToDisk(data map[interface{}]interface{}) error {
|
|
||||||
private, config := filterDottedKeys(data, []string{
|
|
||||||
"rancher.ssh",
|
|
||||||
"rancher.docker.ca_key",
|
|
||||||
"rancher.docker.ca_cert",
|
|
||||||
"rancher.docker.server_key",
|
|
||||||
"rancher.docker.server_cert",
|
|
||||||
})
|
|
||||||
|
|
||||||
err := WriteToFile(config, CloudConfigFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return WriteToFile(private, CloudConfigPrivateFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func readConfig(bytes []byte, substituteMetadataVars bool, files ...string) (map[interface{}]interface{}, error) {
|
func readConfig(bytes []byte, substituteMetadataVars bool, files ...string) (map[interface{}]interface{}, error) {
|
||||||
// You can't just overlay yaml bytes on to maps, it won't merge, but instead
|
// You can't just overlay yaml bytes on to maps, it won't merge, but instead
|
||||||
// just override the keys and not merge the map values.
|
// just override the keys and not merge the map values.
|
||||||
@ -267,7 +265,7 @@ func readConfig(bytes []byte, substituteMetadataVars bool, files ...string) (map
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
left = util.MapsUnion(left, right)
|
left = util.Merge(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes != nil && len(bytes) > 0 {
|
if bytes != nil && len(bytes) > 0 {
|
||||||
@ -279,7 +277,7 @@ func readConfig(bytes []byte, substituteMetadataVars bool, files ...string) (map
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
left = util.MapsUnion(left, right)
|
left = util.Merge(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
return left, nil
|
return left, nil
|
||||||
|
@ -36,7 +36,6 @@ const (
|
|||||||
OsConfigFile = "/usr/share/ros/os-config.yml"
|
OsConfigFile = "/usr/share/ros/os-config.yml"
|
||||||
CloudConfigDir = "/var/lib/rancher/conf/cloud-config.d"
|
CloudConfigDir = "/var/lib/rancher/conf/cloud-config.d"
|
||||||
CloudConfigBootFile = "/var/lib/rancher/conf/cloud-config.d/boot.yml"
|
CloudConfigBootFile = "/var/lib/rancher/conf/cloud-config.d/boot.yml"
|
||||||
CloudConfigPrivateFile = "/var/lib/rancher/conf/cloud-config.d/private.yml"
|
|
||||||
CloudConfigNetworkFile = "/var/lib/rancher/conf/cloud-config.d/network.yml"
|
CloudConfigNetworkFile = "/var/lib/rancher/conf/cloud-config.d/network.yml"
|
||||||
CloudConfigScriptFile = "/var/lib/rancher/conf/cloud-config-script"
|
CloudConfigScriptFile = "/var/lib/rancher/conf/cloud-config-script"
|
||||||
MetaDataFile = "/var/lib/rancher/conf/metadata"
|
MetaDataFile = "/var/lib/rancher/conf/metadata"
|
||||||
@ -48,6 +47,13 @@ var (
|
|||||||
VERSION string
|
VERSION string
|
||||||
ARCH string
|
ARCH string
|
||||||
SUFFIX string
|
SUFFIX string
|
||||||
|
PrivateKeys = []string{
|
||||||
|
"rancher.ssh",
|
||||||
|
"rancher.docker.ca_key",
|
||||||
|
"rancher.docker.ca_cert",
|
||||||
|
"rancher.docker.server_key",
|
||||||
|
"rancher.docker.server_cert",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -108,8 +108,7 @@ func (s *Service) shouldRebuild(ctx context.Context) (bool, error) {
|
|||||||
rebuilding := false
|
rebuilding := false
|
||||||
if outOfSync {
|
if outOfSync {
|
||||||
if cfg.Rancher.ForceConsoleRebuild && s.Name() == "console" {
|
if cfg.Rancher.ForceConsoleRebuild && s.Name() == "console" {
|
||||||
cfg.Rancher.ForceConsoleRebuild = false
|
if err := config.Set("rancher.force_console_rebuild", false); err != nil {
|
||||||
if err := cfg.Save(); err != nil {
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
rebuilding = true
|
rebuilding = true
|
||||||
|
@ -204,7 +204,7 @@ func RunInit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Rancher.Debug {
|
if cfg.Rancher.Debug {
|
||||||
cfgString, err := config.Dump(false, true)
|
cfgString, err := config.Export(false, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{"err": err}).Error("Error serializing config")
|
log.WithFields(log.Fields{"err": err}).Error("Error serializing config")
|
||||||
} else {
|
} else {
|
||||||
|
142
util/util.go
142
util/util.go
@ -3,15 +3,12 @@ package util
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
|
||||||
"reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AnyMap map[interface{}]interface{}
|
type AnyMap map[interface{}]interface{}
|
||||||
@ -91,56 +88,7 @@ func Copy(d interface{}) interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Replace(l, r interface{}) interface{} {
|
func Merge(left, right map[interface{}]interface{}) map[interface{}]interface{} {
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func Equal(l, r interface{}) interface{} {
|
|
||||||
if reflect.DeepEqual(l, r) {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Filter(xs []interface{}, p func(x interface{}) bool) []interface{} {
|
|
||||||
return FlatMap(xs, func(x interface{}) []interface{} {
|
|
||||||
if p(x) {
|
|
||||||
return []interface{}{x}
|
|
||||||
}
|
|
||||||
return []interface{}{}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func FilterStrings(xs []string, p func(x string) bool) []string {
|
|
||||||
return FlatMapStrings(xs, func(x string) []string {
|
|
||||||
if p(x) {
|
|
||||||
return []string{x}
|
|
||||||
}
|
|
||||||
return []string{}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Map(xs []interface{}, f func(x interface{}) interface{}) []interface{} {
|
|
||||||
return FlatMap(xs, func(x interface{}) []interface{} { return []interface{}{f(x)} })
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlatMap(xs []interface{}, f func(x interface{}) []interface{}) []interface{} {
|
|
||||||
result := []interface{}{}
|
|
||||||
for _, x := range xs {
|
|
||||||
result = append(result, f(x)...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlatMapStrings(xs []string, f func(x string) []string) []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, x := range xs {
|
|
||||||
result = append(result, f(x)...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MapsUnion(left, right map[interface{}]interface{}) map[interface{}]interface{} {
|
|
||||||
result := MapCopy(left)
|
result := MapCopy(left)
|
||||||
|
|
||||||
for k, r := range right {
|
for k, r := range right {
|
||||||
@ -149,12 +97,12 @@ func MapsUnion(left, right map[interface{}]interface{}) map[interface{}]interfac
|
|||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
switch r := r.(type) {
|
switch r := r.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
result[k] = MapsUnion(l, r)
|
result[k] = Merge(l, r)
|
||||||
default:
|
default:
|
||||||
result[k] = Replace(l, r)
|
result[k] = r
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
result[k] = Replace(l, r)
|
result[k] = r
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result[k] = Copy(r)
|
result[k] = Copy(r)
|
||||||
@ -164,66 +112,6 @@ func MapsUnion(left, right map[interface{}]interface{}) map[interface{}]interfac
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapsDifference(left, right map[interface{}]interface{}) map[interface{}]interface{} {
|
|
||||||
result := map[interface{}]interface{}{}
|
|
||||||
|
|
||||||
for k, l := range left {
|
|
||||||
if r, ok := right[k]; ok {
|
|
||||||
switch l := l.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
switch r := r.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
if len(l) == 0 && len(r) == 0 {
|
|
||||||
continue
|
|
||||||
} else if len(l) == 0 {
|
|
||||||
result[k] = l
|
|
||||||
} else if v := MapsDifference(l, r); len(v) > 0 {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if v := Equal(l, r); v == nil {
|
|
||||||
result[k] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if v := Equal(l, r); v == nil {
|
|
||||||
result[k] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result[k] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MapsIntersection(left, right map[interface{}]interface{}) map[interface{}]interface{} {
|
|
||||||
result := map[interface{}]interface{}{}
|
|
||||||
|
|
||||||
for k, l := range left {
|
|
||||||
if r, ok := right[k]; ok {
|
|
||||||
switch l := l.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
switch r := r.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
result[k] = MapsIntersection(l, r)
|
|
||||||
default:
|
|
||||||
if v := Equal(l, r); v != nil {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if v := Equal(l, r); v != nil {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func MapCopy(data map[interface{}]interface{}) map[interface{}]interface{} {
|
func MapCopy(data map[interface{}]interface{}) map[interface{}]interface{} {
|
||||||
result := map[interface{}]interface{}{}
|
result := map[interface{}]interface{}{}
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
@ -240,6 +128,16 @@ func SliceCopy(data []interface{}) []interface{} {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RemoveString(slice []string, s string) []string {
|
||||||
|
result := []string{}
|
||||||
|
for _, elem := range slice {
|
||||||
|
if elem != s {
|
||||||
|
result = append(result, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func ToStrings(data []interface{}) []string {
|
func ToStrings(data []interface{}) []string {
|
||||||
result := make([]string, len(data), len(data))
|
result := make([]string, len(data), len(data))
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
@ -248,18 +146,6 @@ func ToStrings(data []interface{}) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func DirLs(dir string) ([]interface{}, error) {
|
|
||||||
result := []interface{}{}
|
|
||||||
files, err := ioutil.ReadDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
for _, f := range files {
|
|
||||||
result = append(result, f)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Map2KVPairs(m map[string]string) []string {
|
func Map2KVPairs(m map[string]string) []string {
|
||||||
r := make([]string, 0, len(m))
|
r := make([]string, 0, len(m))
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
@ -12,15 +12,6 @@ type testCloudConfig struct {
|
|||||||
Key2 string `yaml:"key2,omitempty"`
|
Key2 string `yaml:"key2,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPassByValue(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
cc0ptr := &testCloudConfig{}
|
|
||||||
cc0ptr.Hostname = "test0"
|
|
||||||
cc1 := *cc0ptr
|
|
||||||
cc1.Hostname = "test1"
|
|
||||||
assert.NotEqual(cc0ptr.Hostname, cc1.Hostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertMergesLeftIntoRight(t *testing.T) {
|
func TestConvertMergesLeftIntoRight(t *testing.T) {
|
||||||
assert := require.New(t)
|
assert := require.New(t)
|
||||||
cc0 := testCloudConfig{Key1: "k1v0", Key2: "k2v0"}
|
cc0 := testCloudConfig{Key1: "k1v0", Key2: "k2v0"}
|
||||||
@ -30,13 +21,6 @@ func TestConvertMergesLeftIntoRight(t *testing.T) {
|
|||||||
assert.Equal(expected, cc0)
|
assert.Equal(expected, cc0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNilMap(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
var m map[string]interface{} = nil
|
|
||||||
assert.True(m == nil)
|
|
||||||
assert.True(len(m) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NoTestCopyPointer(t *testing.T) {
|
func NoTestCopyPointer(t *testing.T) {
|
||||||
assert := require.New(t)
|
assert := require.New(t)
|
||||||
testCCpt := &testCloudConfig{}
|
testCCpt := &testCloudConfig{}
|
||||||
@ -48,35 +32,6 @@ func NoTestCopyPointer(t *testing.T) {
|
|||||||
assert.Equal("", m1["b"].(*testCloudConfig).Hostname)
|
assert.Equal("", m1["b"].(*testCloudConfig).Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyMap(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
m := map[interface{}]interface{}{}
|
|
||||||
assert.True(len(m) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryMutateArg(p *string) *string {
|
|
||||||
s := "test"
|
|
||||||
p = &s
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMutableArg(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
s := "somestring"
|
|
||||||
p := &s
|
|
||||||
assert.NotEqual(tryMutateArg(p), p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilter(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
ss := []interface{}{"1", "2", "3", "4"}
|
|
||||||
assert.Equal([]interface{}{"1", "2", "4"}, Filter(ss, func(x interface{}) bool { return x != "3" }))
|
|
||||||
|
|
||||||
ss1 := append([]interface{}{}, "qqq")
|
|
||||||
assert.Equal([]interface{}{"qqq"}, ss1)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapCopy(t *testing.T) {
|
func TestMapCopy(t *testing.T) {
|
||||||
assert := require.New(t)
|
assert := require.New(t)
|
||||||
m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"}
|
m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"}
|
||||||
@ -109,52 +64,7 @@ func TestSliceCopy(t *testing.T) {
|
|||||||
assert.Equal(len(b1), len(b0)+1)
|
assert.Equal(len(b1), len(b0)+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapsIntersection(t *testing.T) {
|
func TestMerge(t *testing.T) {
|
||||||
assert := require.New(t)
|
|
||||||
|
|
||||||
m0 := map[interface{}]interface{}{
|
|
||||||
"a": 1,
|
|
||||||
"b": map[interface{}]interface{}{"c": 3},
|
|
||||||
"d": "4",
|
|
||||||
"e": []interface{}{1, 2, 3},
|
|
||||||
}
|
|
||||||
m1 := MapCopy(m0)
|
|
||||||
|
|
||||||
delete(m0, "a")
|
|
||||||
b1 := m1["b"].(map[interface{}]interface{})
|
|
||||||
delete(b1, "c")
|
|
||||||
m1["e"] = []interface{}{2, 3, 4}
|
|
||||||
expected := map[interface{}]interface{}{"b": map[interface{}]interface{}{}, "d": "4"}
|
|
||||||
assert.Equal(expected, MapsIntersection(m0, m1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapsDifference(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
|
||||||
|
|
||||||
m0 := map[interface{}]interface{}{
|
|
||||||
"a": 1,
|
|
||||||
"b": map[interface{}]interface{}{"c": 3},
|
|
||||||
"d": "4",
|
|
||||||
"e": []interface{}{1, 2, 3},
|
|
||||||
}
|
|
||||||
m1 := MapCopy(m0)
|
|
||||||
|
|
||||||
assert.Equal(map[interface{}]interface{}{}, MapsDifference(m0, m0))
|
|
||||||
assert.Equal(map[interface{}]interface{}{}, MapsDifference(m0, m1))
|
|
||||||
|
|
||||||
delete(m1, "a")
|
|
||||||
b1 := m1["b"].(map[interface{}]interface{})
|
|
||||||
delete(b1, "c")
|
|
||||||
m1["e"] = []interface{}{2, 3, 4}
|
|
||||||
|
|
||||||
expectedM1M0 := map[interface{}]interface{}{"b": map[interface{}]interface{}{}, "e": []interface{}{2, 3, 4}}
|
|
||||||
assert.Equal(expectedM1M0, MapsDifference(m1, m0))
|
|
||||||
|
|
||||||
expectedM0M1 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "e": []interface{}{1, 2, 3}}
|
|
||||||
assert.Equal(expectedM0M1, MapsDifference(m0, m1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapsUnion(t *testing.T) {
|
|
||||||
assert := require.New(t)
|
assert := require.New(t)
|
||||||
|
|
||||||
m0 := map[interface{}]interface{}{
|
m0 := map[interface{}]interface{}{
|
||||||
@ -178,5 +88,5 @@ func TestMapsUnion(t *testing.T) {
|
|||||||
"e": "added",
|
"e": "added",
|
||||||
"f": []interface{}{2, 3, 4},
|
"f": []interface{}{2, 3, 4},
|
||||||
}
|
}
|
||||||
assert.Equal(expected, MapsUnion(m0, m1))
|
assert.Equal(expected, Merge(m0, m1))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user