1
0
mirror of https://github.com/rancher/os.git synced 2025-09-04 16:21:07 +00:00

Refactor configuration

This commit is contained in:
Darren Shepherd
2015-03-14 21:27:04 -07:00
parent 0f8eb3e21d
commit ac2459ba37
10 changed files with 600 additions and 303 deletions

View File

@@ -51,7 +51,7 @@ func disable(c *cli.Context) {
} }
if changed { if changed {
if err = cfg.Save(); err != nil { if err = cfg.Set("enabled_addons", cfg.EnabledAddons); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
@@ -72,7 +72,7 @@ func enable(c *cli.Context) {
} }
if changed { if changed {
if err = cfg.Save(); err != nil { if err = cfg.Set("enabled_addons", cfg.EnabledAddons); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
"github.com/rancherio/os/docker"
) )
func configSubcommands() []cli.Command { func configSubcommands() []cli.Command {
@@ -30,7 +29,7 @@ func configSubcommands() []cli.Command {
{ {
Name: "import", Name: "import",
Usage: "import configuration from standard in or a file", Usage: "import configuration from standard in or a file",
Action: configImport, Action: runImport,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "input, i", Name: "input, i",
@@ -40,43 +39,40 @@ func configSubcommands() []cli.Command {
}, },
{ {
Name: "export", Name: "export",
Usage: "dump full configuration", Usage: "export configuration",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "output, o", Name: "output, o",
Usage: "File to which to save", Usage: "File to which to save",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "full", Name: "private, p",
Usage: "Include full configuration, not just writable fields", Usage: "Include private information such as keys",
},
cli.BoolFlag{
Name: "full, f",
Usage: "Include full configuration, including internal and default settings",
}, },
}, },
Action: configSave, Action: export,
},
{
Name: "merge",
Usage: "merge configuration from stdin",
Action: merge,
}, },
} }
} }
func getConfigData() (map[interface{}]interface{}, error) { func runImport(c *cli.Context) {
cfg, err := config.LoadConfig() var input io.ReadCloser
if err != nil {
log.Fatal(err)
}
content, err := cfg.Dump()
if err != nil {
log.Fatal(err)
}
data := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(content), data)
return data, err
}
func configImport(c *cli.Context) {
var input io.Reader
var err error var err error
input = os.Stdin input = os.Stdin
cfg, err := config.LoadConfig()
if err != nil {
log.Fatal(err)
}
inputFile := c.String("input") inputFile := c.String("input")
if inputFile != "" { if inputFile != "" {
@@ -84,6 +80,7 @@ func configImport(c *cli.Context) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer input.Close()
} }
bytes, err := ioutil.ReadAll(input) bytes, err := ioutil.ReadAll(input)
@@ -91,38 +88,12 @@ func configImport(c *cli.Context) {
log.Fatal(err) log.Fatal(err)
} }
err = mergeConfig(bytes) err = cfg.Import(bytes)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
func mergeConfig(bytes []byte) error {
var newConfig config.Config
err := yaml.Unmarshal(bytes, &newConfig)
if err != nil {
return err
}
cfg, err := config.LoadConfig()
if err != nil {
return err
}
_, err = cfg.Merge(newConfig)
if err != nil {
return err
}
err = cfg.Save()
if err != nil {
return err
}
return err
}
func configSet(c *cli.Context) { func configSet(c *cli.Context) {
key := c.Args().Get(0) key := c.Args().Get(0)
value := c.Args().Get(1) value := c.Args().Get(1)
@@ -130,15 +101,12 @@ func configSet(c *cli.Context) {
return return
} }
data, err := getConfigData() cfg, err := config.LoadConfig()
getOrSetVal(key, data, value)
bytes, err := yaml.Marshal(data)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = mergeConfig(bytes) err = cfg.Set(key, value)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -150,12 +118,15 @@ func configGet(c *cli.Context) {
return return
} }
data, err := getConfigData() cfg, err := config.LoadConfig()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
val := getOrSetVal(arg, data, nil) val, err := cfg.Get(arg)
if err != nil {
log.Fatal(err)
}
printYaml := false printYaml := false
switch val.(type) { switch val.(type) {
@@ -183,6 +154,7 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{
val, ok := data[part] val, ok := data[part]
last := i+1 == len(parts) last := i+1 == len(parts)
// Reached end, set the value
if last && value != nil { if last && value != nil {
if s, ok := value.(string); ok { if s, ok := value.(string); ok {
value = config.DummyMarshall(s) value = config.DummyMarshall(s)
@@ -192,6 +164,14 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{
return value return value
} }
// Missing intermediate key, create key
if !last && value != nil && !ok {
newData := map[interface{}]interface{}{}
data[part] = newData
data = newData
continue
}
if !ok { if !ok {
break break
} }
@@ -211,26 +191,29 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{
return "" return ""
} }
func configSave(c *cli.Context) { func merge(c *cli.Context) {
bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
cfg, err := config.LoadConfig() cfg, err := config.LoadConfig()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
//TODO: why doesn't this work err = cfg.Merge(bytes)
for _, c := range cfg.SystemContainers { if err != nil {
container := docker.NewContainer("", &c) log.Fatal(err)
if container.Err != nil {
log.Fatalf("Failed to parse [%s] : %v", c.Cmd, container.Err)
}
} }
}
if !c.Bool("full") { func export(c *cli.Context) {
cfg.ClearReadOnly() content, err := config.Dump(c.Bool("private"), c.Bool("full"))
if err != nil {
log.Fatal(err)
} }
content, err := cfg.Dump()
output := c.String("output") output := c.String("output")
if output == "" { if output == "" {
fmt.Println(content) fmt.Println(content)
@@ -240,5 +223,4 @@ func configSave(c *cli.Context) {
log.Fatal(err) log.Fatal(err)
} }
} }
} }

View File

@@ -3,8 +3,6 @@ package config
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"regexp"
"strconv"
"strings" "strings"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
@@ -12,90 +10,10 @@ import (
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
const ( func (c *Config) privilegedMerge(newConfig Config) error {
VERSION = "0.0.1" err := c.overlay(newConfig)
CONSOLE_CONTAINER = "console"
DOCKER_BIN = "/usr/bin/docker"
DOCKER_SYSTEM_HOST = "unix:///var/run/system-docker.sock"
DOCKER_HOST = "unix:///var/run/docker.sock"
IMAGES_PATH = "/"
IMAGES_PATTERN = "images*.tar"
SYS_INIT = "/sbin/init-sys"
USER_INIT = "/sbin/init-user"
MODULES_ARCHIVE = "/modules.tar"
DEBUG = false
)
var (
ConfigFile = "/var/lib/rancher/conf/rancher.yml"
)
type InitFunc func(*Config) error
type ContainerConfig struct {
Id string `yaml:"id,omitempty"`
Cmd string `yaml:"run,omitempty"`
MigrateVolumes bool `yaml:"migrate_volumes,omitempty"`
ReloadConfig bool `yaml:"reload_config,omitempty"`
}
type Config struct {
Debug bool `yaml:"debug,omitempty"`
Disable []string `yaml:"disable,omitempty"`
Dns []string `yaml:"dns,flow,omitempty"`
Rescue bool `yaml:"rescue,omitempty"`
RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"`
State ConfigState `yaml:"state,omitempty"`
Userdocker UserDockerInfo `yaml:"userdocker,omitempty"`
OsUpgradeChannel string `yaml:"os_upgrade_channel,omitempty"`
SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"`
SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"`
Modules []string `yaml:"modules,omitempty"`
CloudInit CloudInit `yaml:"cloud_init"`
SshInfo SshInfo `yaml:"ssh"`
EnabledAddons []string `yaml:"enabledAddons,omitempty"`
Addons map[string]Config `yaml:"addons,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"`
}
type NetworkConfig struct {
Interfaces []InterfaceConfig `yaml:"interfaces"`
PostRun *ContainerConfig `yaml:"post_run"`
}
type InterfaceConfig struct {
Match string `yaml:"match"`
DHCP bool `yaml:"dhcp"`
Address string `yaml:"address,omitempty"`
Gateway string `yaml:"gateway,omitempty"`
MTU int `yaml:"mtu,omitempty"`
}
type UserDockerInfo struct {
UseTLS bool `yaml:"use_tls"`
TLSServerCert string `yaml:"tls_server_cert"`
TLSServerKey string `yaml:"tls_server_key"`
TLSCACert string `yaml:"tls_ca_cert"`
}
type SshInfo struct {
Keys map[string]string
}
type ConfigState struct {
FsType string `yaml:"fstype"`
Dev string `yaml:"dev"`
Required bool `yaml:"required"`
}
type CloudInit struct {
Datasources []string `yaml:"datasources,omitempty"`
}
func (c *Config) PrivilegedMerge(newConfig Config) (bool, error) {
reboot, err := c.Merge(newConfig)
if err != nil { if err != nil {
return reboot, err return err
} }
toAppend := make([]ContainerConfig, 0, 5) toAppend := make([]ContainerConfig, 0, 5)
@@ -115,48 +33,56 @@ func (c *Config) PrivilegedMerge(newConfig Config) (bool, error) {
c.SystemContainers = append(c.SystemContainers, toAppend...) c.SystemContainers = append(c.SystemContainers, toAppend...)
return reboot, nil return nil
} }
func (c *Config) Merge(newConfig Config) (bool, error) { func (c *Config) overlay(newConfig Config) error {
//Efficient? Nope, but computers are fast newConfig.clearReadOnly()
newConfig.ClearReadOnly() return util.Convert(&newConfig, c)
content, err := newConfig.Dump() }
if err != nil {
return false, err func (c *Config) clearReadOnly() {
c.BootstrapContainers = make([]ContainerConfig, 0)
c.SystemContainers = make([]ContainerConfig, 0)
}
func clearReadOnly(data map[interface{}]interface{}) map[interface{}]interface{} {
newData := make(map[interface{}]interface{})
for k, v := range data {
newData[k] = v
} }
log.Debugf("Input \n%s", string(content)) delete(newData, "system_container")
delete(newData, "bootstrap_container")
err = yaml.Unmarshal([]byte(content), c) return newData
return true, err
} }
func (c *Config) ClearReadOnly() { func (c *Config) Import(bytes []byte) error {
c.SystemContainers = []ContainerConfig{} data, err := readConfig(bytes, PrivateConfigFile)
c.RescueContainer = nil
c.Rescue = false
c.Debug = false
c.Addons = map[string]Config{}
}
func (c *Config) Dump() (string, error) {
content, err := yaml.Marshal(c)
if err != nil {
return "", err
} else {
return string(content), err
}
}
func (c Config) Save() error {
c.ClearReadOnly()
content, err := c.Dump()
if err != nil { if err != nil {
return err return err
} }
return ioutil.WriteFile(ConfigFile, []byte(content), 400) if err = saveToDisk(data); err != nil {
return err
}
return c.Reload()
}
func (c *Config) Merge(bytes []byte) error {
data, err := readSavedConfig(bytes)
if err != nil {
return err
}
err = saveToDisk(data)
if err != nil {
return err
}
return c.Reload()
} }
func LoadConfig() (*Config, error) { func LoadConfig() (*Config, error) {
@@ -172,17 +98,6 @@ func LoadConfig() (*Config, error) {
return cfg, nil return cfg, nil
} }
func FilterGlobalConfig(input []string) []string {
result := make([]string, 0, len(input))
for _, value := range input {
if !strings.HasPrefix(value, "--rancher") {
result = append(result, value)
}
}
return result
}
func (c *Config) readArgs() error { func (c *Config) readArgs() error {
log.Debug("Reading config args") log.Debug("Reading config args")
parts := make([]string, len(os.Args)) parts := make([]string, len(os.Args))
@@ -209,32 +124,13 @@ func (c *Config) readArgs() error {
return c.merge(cmdLineObj) return c.merge(cmdLineObj)
} }
func (c *Config) merge(values map[string]interface{}) error { func (c *Config) merge(values map[interface{}]interface{}) error {
// Lazy way to assign values to *Config values = clearReadOnly(values)
override, err := yaml.Marshal(values) return util.Convert(values, c)
if err != nil {
return err
}
var newConfig Config
err = yaml.Unmarshal(override, &newConfig)
if err != nil {
return nil
}
_, err = c.Merge(newConfig)
return err
} }
func (c *Config) readFile() error { func (c *Config) readFiles() error {
content, err := ioutil.ReadFile(ConfigFile) data, err := readSavedConfig(nil)
if os.IsNotExist(err) {
return nil
} else if err != nil {
return err
}
data := make(map[string]interface{})
err = yaml.Unmarshal(content, data)
if err != nil { if err != nil {
return err return err
} }
@@ -259,75 +155,49 @@ func (c *Config) readCmdline() error {
return c.merge(cmdLineObj) return c.merge(cmdLineObj)
} }
func DummyMarshall(value string) interface{} { func Dump(private, full bool) (string, error) {
if strings.HasPrefix(value, "[") && strings.HasSuffix(value, "]") { files := []string{ConfigFile}
return strings.Split(value[1:len(value)-1], ",") if private {
files = append(files, PrivateConfigFile)
} }
if value == "true" { var c Config
return true
} else if value == "false" { if full {
return false c = *NewConfig()
} else if ok, _ := regexp.MatchString("^[0-9]+$", value); ok {
i, err := strconv.Atoi(value)
if err != nil {
panic(err)
}
return i
} }
return value data, err := readConfig(nil, files...)
if err != nil {
return "", err
}
err = c.merge(data)
if err != nil {
return "", err
}
err = c.readGlobals()
if err != nil {
return "", err
}
bytes, err := yaml.Marshal(c)
return string(bytes), err
} }
func parseCmdline(cmdLine string) map[string]interface{} { func (c *Config) readGlobals() error {
result := make(map[string]interface{}) return util.ShortCircuit(
c.readCmdline,
outer: c.readArgs,
for _, part := range strings.Split(cmdLine, " ") { c.mergeAddons,
if !strings.HasPrefix(part, "rancher.") { )
continue
}
var value string
kv := strings.SplitN(part, "=", 2)
if len(kv) == 1 {
value = "true"
} else {
value = kv[1]
}
current := result
keys := strings.Split(kv[0], ".")[1:]
for i, key := range keys {
if i == len(keys)-1 {
current[key] = DummyMarshall(value)
} else {
if obj, ok := current[key]; ok {
if newCurrent, ok := obj.(map[string]interface{}); ok {
current = newCurrent
} else {
continue outer
}
} else {
newCurrent := make(map[string]interface{})
current[key] = newCurrent
current = newCurrent
}
}
}
}
log.Debugf("Input obj %s", result)
return result
} }
func (c *Config) Reload() error { func (c *Config) Reload() error {
return util.ShortCircuit( return util.ShortCircuit(
c.readFile, c.readFiles,
c.readCmdline, c.readGlobals,
c.readArgs,
c.mergeAddons,
) )
} }
@@ -335,8 +205,7 @@ func (c *Config) mergeAddons() error {
for _, addon := range c.EnabledAddons { for _, addon := range c.EnabledAddons {
if newConfig, ok := c.Addons[addon]; ok { if newConfig, ok := c.Addons[addon]; ok {
log.Debugf("Enabling addon %s", addon) log.Debugf("Enabling addon %s", addon)
_, err := c.PrivilegedMerge(newConfig) if err := c.privilegedMerge(newConfig); err != nil {
if err != nil {
return err return err
} }
} }
@@ -345,14 +214,53 @@ func (c *Config) mergeAddons() error {
return nil return nil
} }
func RunInitFuncs(cfg *Config, initFuncs []InitFunc) error { func (c *Config) Get(key string) (interface{}, error) {
for i, initFunc := range initFuncs { data := make(map[interface{}]interface{})
log.Debugf("[%d/%d] Starting", i+1, len(initFuncs)) err := util.Convert(c, &data)
if err := initFunc(cfg); err != nil { if err != nil {
log.Errorf("Failed [%d/%d] %d%%", i+1, len(initFuncs), ((i + 1) * 100 / len(initFuncs))) return nil, err
return err
}
log.Debugf("[%d/%d] Done %d%%", i+1, len(initFuncs), ((i + 1) * 100 / len(initFuncs)))
} }
return nil
return getOrSetVal(key, data, nil), nil
}
//func (c *Config) SetBytes(bytes []byte) error {
// content, err := readConfigFile()
// if err != nil {
// return err
// }
//
// data := make(map[interface{}]interface{})
// err = yaml.Unmarshal(content, &data)
// if err != nil {
// return err
// }
//
// err = yaml.Unmarshal(bytes, &data)
// if err != nil {
// return err
// }
//
// content, err = yaml.Marshal(data)
// if err != nil {
// return err
// }
//
// return ioutil.WriteFile(ConfigFile, content, 400)
//}
func (c *Config) Set(key string, value interface{}) error {
data, err := readSavedConfig(nil)
if err != nil {
return err
}
getOrSetVal(key, data, value)
err = saveToDisk(data)
if err != nil {
return err
}
return c.Reload()
} }

View File

@@ -1,10 +1,7 @@
package config package config
import ( import "testing"
"testing" import "reflect"
"code.google.com/p/rog-go/deepdiff"
)
func TestParseCmdline(t *testing.T) { func TestParseCmdline(t *testing.T) {
expected := map[string]interface{}{ expected := map[string]interface{}{
@@ -23,8 +20,76 @@ func TestParseCmdline(t *testing.T) {
actual := parseCmdline("a b rancher.rescue rancher.keyArray=[1,2] rancher.key1=value1 c rancher.key2=value2 rancher.obj1.key3=3value rancher.obj1.obj2.key4 rancher.key5=5") actual := parseCmdline("a b rancher.rescue rancher.keyArray=[1,2] rancher.key1=value1 c rancher.key2=value2 rancher.obj1.key3=3value rancher.obj1.obj2.key4 rancher.key5=5")
ok, err := deepdiff.DeepDiff(actual, expected) ok := reflect.DeepEqual(actual, expected)
if !ok || err != nil { if !ok {
t.Fatal(err) t.Fatalf("%v != %v", actual, expected)
}
}
func TestGet(t *testing.T) {
data := map[interface{}]interface{}{
"key": "value",
"key2": map[interface{}]interface{}{
"subkey": "subvalue",
"subnum": 42,
},
}
tests := map[string]interface{}{
"key": "value",
"key2.subkey": "subvalue",
"key2.subnum": 42,
"key2.subkey2": "",
"foo": "",
}
for k, v := range tests {
if getOrSetVal(k, data, nil) != v {
t.Fatalf("Expected %v, got %v, for key %s", v, getOrSetVal(k, data, nil), k)
}
}
}
func TestSet(t *testing.T) {
data := map[interface{}]interface{}{
"key": "value",
"key2": map[interface{}]interface{}{
"subkey": "subvalue",
"subnum": 42,
},
}
expected := map[interface{}]interface{}{
"key": "value2",
"key2": map[interface{}]interface{}{
"subkey": "subvalue2",
"subkey2": "value",
"subkey3": 43,
"subnum": 42,
},
"key3": map[interface{}]interface{}{
"subkey3": 44,
},
"key4": "value4",
}
tests := map[string]interface{}{
"key": "value2",
"key2.subkey": "subvalue2",
"key2.subkey2": "value",
"key2.subkey3": 43,
"key3.subkey3": 44,
"key4": "value4",
}
for k, v := range tests {
getOrSetVal(k, data, v)
if getOrSetVal(k, data, nil) != v {
t.Fatalf("Expected %v, got %v, for key %s", v, getOrSetVal(k, data, nil), k)
}
}
if !reflect.DeepEqual(data, expected) {
t.Fatalf("Expected %v, got %v", expected, data)
} }
} }

116
config/data_funcs.go Normal file
View File

@@ -0,0 +1,116 @@
package config
import (
log "github.com/Sirupsen/logrus"
"regexp"
"strconv"
"strings"
)
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)
// Reached end, set the value
if last && value != nil {
if s, ok := value.(string); ok {
value = DummyMarshall(s)
}
data[part] = value
return value
}
// Missing intermediate key, create key
if !last && value != nil && !ok {
newData := map[interface{}]interface{}{}
data[part] = newData
data = newData
continue
}
if !ok {
break
}
if last {
return val
}
newData, ok := val.(map[interface{}]interface{})
if !ok {
break
}
data = newData
}
return ""
}
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" {
return false
} else if ok, _ := regexp.MatchString("^[0-9]+$", value); ok {
i, err := strconv.Atoi(value)
if err != nil {
panic(err)
}
return i
}
return value
}
func parseCmdline(cmdLine string) map[interface{}]interface{} {
result := make(map[interface{}]interface{})
outer:
for _, part := range strings.Split(cmdLine, " ") {
if !strings.HasPrefix(part, "rancher.") {
continue
}
var value string
kv := strings.SplitN(part, "=", 2)
if len(kv) == 1 {
value = "true"
} else {
value = kv[1]
}
current := result
keys := strings.Split(kv[0], ".")[1:]
for i, key := range keys {
if i == len(keys)-1 {
current[key] = DummyMarshall(value)
} else {
if obj, ok := current[key]; ok {
if newCurrent, ok := obj.(map[interface{}]interface{}); ok {
current = newCurrent
} else {
continue outer
}
} else {
newCurrent := make(map[interface{}]interface{})
current[key] = newCurrent
current = newCurrent
}
}
}
}
log.Debugf("Input obj %v", result)
return result
}

98
config/disk.go Normal file
View File

@@ -0,0 +1,98 @@
package config
import (
"io/ioutil"
"os"
"github.com/rancherio/os/util"
"gopkg.in/yaml.v2"
)
func writeToFile(data interface{}, filename string) error {
content, err := yaml.Marshal(data)
if err != nil {
return err
}
return ioutil.WriteFile(filename, content, 400)
}
func saveToDisk(data map[interface{}]interface{}) error {
config := make(map[interface{}]interface{})
private := make(map[interface{}]interface{})
for k, v := range data {
if k == "ssh" {
private[k] = v
} else if k == "userdocker" {
var userDockerConfig UserDockerConfig
var userDockerConfigPrivate UserDockerConfig
err := util.Convert(v, &userDockerConfig)
if err != nil {
return err
}
userDockerConfigPrivate.TLSCACert = userDockerConfig.TLSCACert
userDockerConfigPrivate.TLSServerKey = userDockerConfig.TLSServerKey
userDockerConfigPrivate.TLSServerCert = userDockerConfig.TLSServerCert
userDockerConfig.TLSCACert = ""
userDockerConfig.TLSServerKey = ""
userDockerConfig.TLSServerCert = ""
config[k] = userDockerConfig
private[k] = userDockerConfigPrivate
} else {
config[k] = v
}
}
err := writeToFile(config, ConfigFile)
if err != nil {
return err
}
return writeToFile(private, PrivateConfigFile)
}
func readSavedConfig(bytes []byte) (map[interface{}]interface{}, error) {
return readConfig(bytes, ConfigFile, PrivateConfigFile)
}
func readConfig(bytes []byte, files ...string) (map[interface{}]interface{}, error) {
data := make(map[interface{}]interface{})
for _, conf := range files {
content, err := readConfigFile(conf)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(content, &data)
if err != nil {
return nil, err
}
}
if bytes != nil && len(bytes) > 0 {
if err := yaml.Unmarshal(bytes, &data); err != nil {
return nil, err
}
}
return data, nil
}
func readConfigFile(file string) ([]byte, error) {
content, err := ioutil.ReadFile(file)
if err != nil {
if os.IsNotExist(err) {
err = nil
content = []byte{}
} else {
return nil, err
}
}
return content, err
}

32
config/init.go Normal file
View File

@@ -0,0 +1,32 @@
package config
import (
"strings"
log "github.com/Sirupsen/logrus"
)
type InitFunc func(*Config) error
func RunInitFuncs(cfg *Config, initFuncs []InitFunc) error {
for i, initFunc := range initFuncs {
log.Debugf("[%d/%d] Starting", i+1, len(initFuncs))
if err := initFunc(cfg); err != nil {
log.Errorf("Failed [%d/%d] %d%%", i+1, len(initFuncs), ((i + 1) * 100 / len(initFuncs)))
return err
}
log.Debugf("[%d/%d] Done %d%%", i+1, len(initFuncs), ((i + 1) * 100 / len(initFuncs)))
}
return nil
}
func FilterGlobalConfig(input []string) []string {
result := make([]string, 0, len(input))
for _, value := range input {
if !strings.HasPrefix(value, "--rancher") {
result = append(result, value)
}
}
return result
}

86
config/types.go Normal file
View File

@@ -0,0 +1,86 @@
package config
const (
VERSION = "0.0.1"
CONSOLE_CONTAINER = "console"
DOCKER_BIN = "/usr/bin/docker"
DOCKER_SYSTEM_HOST = "unix:///var/run/system-docker.sock"
DOCKER_HOST = "unix:///var/run/docker.sock"
IMAGES_PATH = "/"
IMAGES_PATTERN = "images*.tar"
SYS_INIT = "/sbin/init-sys"
USER_INIT = "/sbin/init-user"
MODULES_ARCHIVE = "/modules.tar"
DEBUG = false
)
var (
ConfigFile = "/var/lib/rancher/conf/rancher.yml"
PrivateConfigFile = "/var/lib/rancher/conf/rancher-private.yml"
)
type ContainerConfig struct {
Id string `yaml:"id,omitempty"`
Cmd string `yaml:"run,omitempty"`
MigrateVolumes bool `yaml:"migrate_volumes,omitempty"`
ReloadConfig bool `yaml:"reload_config,omitempty"`
}
type Config struct {
Debug bool `yaml:"debug,omitempty"`
Disable []string `yaml:"disable,omitempty"`
Dns []string `yaml:"dns,flow,omitempty"`
//Rescue bool `yaml:"rescue,omitempty"`
//RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"`
State ConfigState `yaml:"state,omitempty"`
Userdocker UserDockerConfig `yaml:"userdocker,omitempty"`
UpgradeConfig UpgradeConfig `yaml:"upgrade,omitempty"`
BootstrapContainers []ContainerConfig `yaml:"bootstrap_containers,omitempty"`
SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"`
UserContainers []ContainerConfig `yaml:"user_containers,omitempty"`
SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"`
Modules []string `yaml:"modules,omitempty"`
CloudInit CloudInit `yaml:"cloud_init,omitempty"`
SshConfig SshConfig `yaml:"ssh,omitempty"`
EnabledAddons []string `yaml:"enabled_addons,omitempty"`
Addons map[string]Config `yaml:"addons,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"`
}
type UpgradeConfig struct {
Url string `yaml:"url,omitempty"`
}
type NetworkConfig struct {
Interfaces []InterfaceConfig `yaml:"interfaces,omitempty"`
PostRun *ContainerConfig `yaml:"post_run,omitempty"`
}
type InterfaceConfig struct {
Match string `yaml:"match,omitempty"`
DHCP bool `yaml:"dhcp,omitempty"`
Address string `yaml:"address,omitempty"`
Gateway string `yaml:"gateway,omitempty"`
MTU int `yaml:"mtu,omitempty"`
}
type UserDockerConfig struct {
UseTLS bool `yaml:"use_tls,omitempty"`
TLSServerCert string `yaml:"tls_server_cert,omitempty"`
TLSServerKey string `yaml:"tls_server_key,omitempty"`
TLSCACert string `yaml:"tls_ca_cert,omitempty"`
}
type SshConfig struct {
Keys map[string]string `yaml:"keys,omitempty"`
}
type ConfigState struct {
FsType string `yaml:"fstype,omitempty"`
Dev string `yaml:"dev,omitempty"`
Required bool `yaml:"required,omitempty"`
}
type CloudInit struct {
Datasources []string `yaml:"datasources,omitempty"`
}

View File

@@ -277,7 +277,7 @@ func RunInit() error {
} }
if cfg.Debug { if cfg.Debug {
cfgString, _ := cfg.Dump() cfgString, _ := config.Dump(false, true)
if cfgString != "" { if cfgString != "" {
log.Debugf("Config: %s", cfgString) log.Debugf("Config: %s", cfgString)
} }

View File

@@ -10,6 +10,7 @@ import (
"syscall" "syscall"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"gopkg.in/yaml.v2"
) )
var ( var (
@@ -154,3 +155,12 @@ func RandSeq(n int) string {
} }
return string(b) return string(b)
} }
func Convert(from, to interface{}) error {
bytes, err := yaml.Marshal(from)
if err != nil {
return err
}
return yaml.Unmarshal(bytes, to)
}