diff --git a/cmd/control/config.go b/cmd/control/config.go index 83d1bd1d..2953e716 100644 --- a/cmd/control/config.go +++ b/cmd/control/config.go @@ -20,6 +20,11 @@ func configSubcommands() []cli.Command { Usage: "get value", Action: configGet, }, + { + Name: "set", + Usage: "set a value", + Action: configSet, + }, { Name: "import", Usage: "list values", @@ -42,7 +47,7 @@ func configSubcommands() []cli.Command { } } -func configGet(c *cli.Context) { +func getConfigData() (map[interface{}]interface{}, error) { cfg, err := config.LoadConfig() if err != nil { log.Fatal(err) @@ -54,17 +59,64 @@ func configGet(c *cli.Context) { } data := make(map[interface{}]interface{}) - yaml.Unmarshal([]byte(content), data) + err = yaml.Unmarshal([]byte(content), data) - arg := c.Args().Get(0) - if arg == "" { - fmt.Println("") + return data, err +} + +func configSet(c *cli.Context) { + key := c.Args().Get(0) + value := c.Args().Get(1) + if key == "" { return } - parts := strings.Split(arg, ".") + data, err := getConfigData() + getOrSetVal(key, data, value) - val := lookupVal(parts, data) + bytes, err := yaml.Marshal(data) + if err != nil { + log.Fatal(err) + } + + var newConfig config.Config + err = yaml.Unmarshal(bytes, &newConfig) + if err != nil { + log.Fatal(err) + } + + cfg, err := config.LoadConfig() + if err != nil { + log.Fatal(err) + } + + reboot, err := cfg.Merge(newConfig) + if err != nil { + log.Fatal(err) + } + + err = cfg.Save() + if err != nil { + log.Fatal(err) + } + + if reboot { + fmt.Println("Reboot needed") + } +} + +func configGet(c *cli.Context) { + arg := c.Args().Get(0) + if arg == "" { + return + } + + data, err := getConfigData() + if err != nil { + log.Fatal(err) + } + + val := getOrSetVal(arg, data, nil) printYaml := false switch val.(type) { @@ -85,14 +137,27 @@ func configGet(c *cli.Context) { } } -func lookupVal(parts []string, data map[interface{}]interface{}) interface{} { +func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) interface{} { + parts := strings.Split(args, ".") + for i, part := range parts { val, ok := data[part] + last := i+1 == len(parts) + + if last && value != nil { + if s, ok := value.(string); ok { + value = config.DummyMarshall(s) + } + + data[part] = value + return value + } + if !ok { break } - if i+1 == len(parts) { + if last { return val } diff --git a/config/config.go b/config/config.go index 4df40819..189f022c 100644 --- a/config/config.go +++ b/config/config.go @@ -26,6 +26,10 @@ const ( DEBUG = false ) +var ( + ConfigFile = "/var/lib/rancher/rancher.yml" +) + type InitFunc func(*Config) error type ContainerConfig struct { @@ -40,7 +44,7 @@ type Config struct { //UserContainers []ContainerConfig `yaml:"userContainser,omitempty"` Debug bool `yaml:"debug,omitempty"` Disable []string `yaml:"disable,omitempty"` - Dns []string `yaml:"dns,omitempty"` + Dns []string `yaml:"dns,flow,omitempty"` Rescue bool `yaml:"rescue,omitempty"` RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` State ConfigState `yaml:"state,omitempty"` @@ -55,6 +59,18 @@ type ConfigState struct { Required bool `yaml:"required"` } +func (c *Config) Merge(newConfig Config) (bool, error) { + //Efficient? Nope, but computers are fast + newConfig.ClearReadOnly() + content, err := newConfig.Dump() + if err != nil { + return false, err + } + + err = yaml.Unmarshal([]byte(content), c) + return true, err +} + func (c *Config) ClearReadOnly() { c.SystemContainers = []ContainerConfig{} c.RescueContainer = nil @@ -69,6 +85,16 @@ func (c *Config) Dump() (string, error) { } } +func (c Config) Save() error { + c.ClearReadOnly() + content, err := c.Dump() + if err != nil { + return err + } + + return ioutil.WriteFile(ConfigFile, []byte(content), 400) +} + func LoadConfig() (*Config, error) { cfg := NewConfig() if err := cfg.Reload(); err != nil { @@ -105,6 +131,21 @@ func (c *Config) merge(values map[string]interface{}) error { return yaml.Unmarshal(override, c) } +func (c *Config) readFile() error { + content, err := ioutil.ReadFile(ConfigFile) + if os.IsNotExist(err) { + return nil + } + + data := make(map[string]interface{}) + err = yaml.Unmarshal(content, data) + if err != nil { + return err + } + + return c.merge(data) +} + func (c *Config) readCmdline() error { log.Debug("Reading config cmdline") cmdLine, err := ioutil.ReadFile("/proc/cmdline") @@ -122,7 +163,11 @@ func (c *Config) readCmdline() error { return c.merge(cmdLineObj) } -func dummyMarshall(value string) interface{} { +func DummyMarshall(value string) interface{} { + if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { + return strings.Split(value[1:len(value)-1], ",") + } + if value == "true" { return true } else if value == "false" { @@ -160,11 +205,7 @@ outer: keys := strings.Split(kv[0], ".")[1:] for i, key := range keys { if i == len(keys)-1 { - if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { - current[key] = strings.Split(value[1:len(value)-1], ",") - } else { - current[key] = dummyMarshall(value) - } + current[key] = DummyMarshall(value) } else { if obj, ok := current[key]; ok { if newCurrent, ok := obj.(map[string]interface{}); ok { @@ -187,6 +228,7 @@ outer: func (c *Config) Reload() error { return util.ShortCircuit( + c.readFile, c.readCmdline, c.readArgs, )