diff --git a/.dockerignore b/.dockerignore index bf99dfab..43c4ac91 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,8 @@ .DS_Store .git .idea +bin +gopath tmp state build diff --git a/cmd/cloudinit/cloudinit.go b/cmd/cloudinit/cloudinit.go index 173d6ac9..560457e2 100644 --- a/cmd/cloudinit/cloudinit.go +++ b/cmd/cloudinit/cloudinit.go @@ -48,86 +48,51 @@ const ( datasourceInterval = 100 * time.Millisecond datasourceMaxInterval = 30 * time.Second datasourceTimeout = 5 * time.Minute + sshKeyName = "rancheros-cloud-config" + baseConfigDir = "/var/lib/rancher/conf/cloud-config.d" ) var ( - baseConfigDir string - outputDir string - outputFile string - metaDataFile string - scriptFile string - rancherYml string - save bool - execute bool - network bool - sshKeyName string - flags *flag.FlagSet + save bool + execute bool + network bool + flags *flag.FlagSet ) func init() { flags = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) - flags.StringVar(&baseConfigDir, "base-config-dir", "/var/lib/rancher/conf/cloud-config.d", "base cloud config") - flags.StringVar(&outputDir, "dir", "/var/lib/rancher/conf", "working directory") - flags.StringVar(&outputFile, "file", "cloud-config-processed.yml", "output cloud config file name") - flags.StringVar(&metaDataFile, "metadata", "metadata", "output metdata file name") - flags.StringVar(&scriptFile, "script-file", "cloud-config-script", "output cloud config script file name") - flags.StringVar(&rancherYml, "rancher", "cloud-config-rancher.yml", "output cloud config rancher file name") - flags.StringVar(&sshKeyName, "ssh-key-name", "rancheros-cloud-config", "SSH key name") flags.BoolVar(&network, "network", true, "use network based datasources") flags.BoolVar(&save, "save", false, "save cloud config and exit") flags.BoolVar(&execute, "execute", false, "execute saved cloud config") } func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadata) error { - scriptOutput := path.Join(outputDir, scriptFile) - cloudConfigOutput := path.Join(outputDir, outputFile) - rancherYmlOutput := path.Join(outputDir, rancherYml) - metaDataOutput := path.Join(outputDir, metaDataFile) - - os.Remove(scriptOutput) - os.Remove(cloudConfigOutput) - os.Remove(rancherYmlOutput) - os.Remove(metaDataOutput) + os.Remove(rancherConfig.CloudConfigScriptFile) + os.Remove(rancherConfig.CloudConfigFile) + os.Remove(rancherConfig.MetaDataFile) if len(scriptBytes) > 0 { - log.Infof("Writing to %s", scriptOutput) - if err := ioutil.WriteFile(scriptOutput, scriptBytes, 500); err != nil { - log.Errorf("Error while writing file %s: %v", scriptOutput, err) + log.Infof("Writing to %s", rancherConfig.CloudConfigScriptFile) + if err := ioutil.WriteFile(rancherConfig.CloudConfigScriptFile, scriptBytes, 500); err != nil { + log.Errorf("Error while writing file %s: %v", rancherConfig.CloudConfigScriptFile, err) return err } } - cloudConfigBytes = append([]byte("#cloud-config\n"), cloudConfigBytes...) - log.Infof("Writing to %s", cloudConfigOutput) - if err := ioutil.WriteFile(cloudConfigOutput, cloudConfigBytes, 500); err != nil { - log.Errorf("Error while writing file %s: %v", cloudConfigOutput, err) + if err := ioutil.WriteFile(rancherConfig.CloudConfigFile, cloudConfigBytes, 400); err != nil { return err } - - ccData := make(map[string]interface{}) - if err := yaml.Unmarshal(cloudConfigBytes, ccData); err != nil { - return err - } - - if rancher, ok := ccData["rancher"]; ok { - bytes, err := yaml.Marshal(rancher) - if err != nil { - return err - } - - if err = ioutil.WriteFile(rancherYmlOutput, bytes, 400); err != nil { - return err - } - } + log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigFile, string(cloudConfigBytes)) metaDataBytes, err := yaml.Marshal(metadata) if err != nil { return err } - if err = ioutil.WriteFile(metaDataOutput, metaDataBytes, 400); err != nil { + if err = ioutil.WriteFile(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { return err } + log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes)) return nil } @@ -258,24 +223,22 @@ func saveCloudConfig() error { } func getSaveCloudConfig() (*config.CloudConfig, error) { - cloudConfig := path.Join(outputDir, outputFile) - - ds := file.NewDatasource(cloudConfig) + ds := file.NewDatasource(rancherConfig.CloudConfigFile) if !ds.IsAvailable() { - log.Infof("%s does not exist", cloudConfig) + log.Infof("%s does not exist", rancherConfig.CloudConfigFile) return nil, nil } ccBytes, err := ds.FetchUserdata() if err != nil { - log.Errorf("Failed to read user-data from %s: %v", cloudConfig, err) + log.Errorf("Failed to read user-data from %s: %v", rancherConfig.CloudConfigFile, err) return nil, err } var cc config.CloudConfig err = yaml.Unmarshal(ccBytes, &cc) if err != nil { - log.Errorf("Failed to unmarshall user-data from %s: %v", cloudConfig, err) + log.Errorf("Failed to unmarshall user-data from %s: %v", rancherConfig.CloudConfigFile, err) return nil, err } @@ -290,7 +253,7 @@ func executeCloudConfig() error { var metadata datasource.Metadata - metaDataBytes, err := ioutil.ReadFile(path.Join(outputDir, metaDataFile)) + metaDataBytes, err := ioutil.ReadFile(rancherConfig.MetaDataFile) if err != nil { return err } @@ -338,6 +301,8 @@ func executeCloudConfig() error { func Main() { flags.Parse(rancherConfig.FilterGlobalConfig(os.Args[1:])) + log.Infof("Running cloud-init: save=%v, execute=%v", save, execute) + if save { err := saveCloudConfig() if err != nil { @@ -376,10 +341,10 @@ func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.Cl // getDatasources creates a slice of possible Datasources for cloudinit based // on the different source command-line flags. -func getDatasources(cfg *rancherConfig.Config) []datasource.Datasource { +func getDatasources(cfg *rancherConfig.CloudConfig) []datasource.Datasource { dss := make([]datasource.Datasource, 0, 5) - for _, ds := range cfg.CloudInit.Datasources { + for _, ds := range cfg.Rancher.CloudInit.Datasources { parts := strings.SplitN(ds, ":", 2) switch parts[0] { diff --git a/cmd/control/config.go b/cmd/control/config.go index a0f09ee9..a711d66e 100644 --- a/cmd/control/config.go +++ b/cmd/control/config.go @@ -76,16 +76,16 @@ func configSubcommands() []cli.Command { } } -func imagesFromConfig(cfg *config.Config) []string { +func imagesFromConfig(cfg *config.CloudConfig) []string { imagesMap := map[string]int{} - for _, service := range cfg.BootstrapContainers { + for _, service := range cfg.Rancher.BootstrapContainers { imagesMap[service.Image] = 1 } - for _, service := range cfg.Autoformat { + for _, service := range cfg.Rancher.Autoformat { imagesMap[service.Image] = 1 } - for _, service := range cfg.SystemContainers { + for _, service := range cfg.Rancher.Services { imagesMap[service.Image] = 1 } @@ -165,12 +165,12 @@ func configGet(c *cli.Context) { cfg, err := config.LoadConfig() if err != nil { - log.Fatal(err) + log.Panicln(err) } val, err := cfg.Get(arg) if err != nil { - log.Fatal(err) + log.WithFields(log.Fields{"cfg": cfg, "arg": arg, "val": val}).Panicln(err) } printYaml := false @@ -192,50 +192,6 @@ func configGet(c *cli.Context) { } } -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 = config.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 merge(c *cli.Context) { bytes, err := ioutil.ReadAll(os.Stdin) if err != nil { diff --git a/cmd/control/env.go b/cmd/control/env.go index 7c3d0a15..9e4f9ec7 100644 --- a/cmd/control/env.go +++ b/cmd/control/env.go @@ -20,8 +20,8 @@ func envAction(c *cli.Context) { args := c.Args() osEnv := os.Environ() - envMap := make(map[string]string, len(cfg.Environment)+len(osEnv)) - for k, v := range cfg.Environment { + envMap := make(map[string]string, len(cfg.Rancher.Environment)+len(osEnv)) + for k, v := range cfg.Rancher.Environment { envMap[k] = v } for k, v := range util.KVPairs2Map(osEnv) { diff --git a/cmd/control/os.go b/cmd/control/os.go index 7fe2b4ee..76d4f7cd 100644 --- a/cmd/control/os.go +++ b/cmd/control/os.go @@ -249,5 +249,5 @@ func getUpgradeUrl() (string, error) { return "", err } - return cfg.Upgrade.Url, nil + return cfg.Rancher.Upgrade.Url, nil } diff --git a/cmd/control/service.go b/cmd/control/service.go index 18b483c0..9c46b9b7 100644 --- a/cmd/control/service.go +++ b/cmd/control/service.go @@ -2,7 +2,7 @@ package control import ( "fmt" - "log" + log "github.com/Sirupsen/logrus" "strings" "github.com/codegangsta/cli" @@ -44,16 +44,16 @@ func disable(c *cli.Context) { } for _, service := range c.Args() { - if _, ok := cfg.ServicesInclude[service]; !ok { + if _, ok := cfg.Rancher.ServicesInclude[service]; !ok { continue } - cfg.ServicesInclude[service] = false + cfg.Rancher.ServicesInclude[service] = false changed = true } if changed { - if err = cfg.Set("services_include", cfg.ServicesInclude); err != nil { + if err = cfg.Set("rancher.services_include", cfg.Rancher.ServicesInclude); err != nil { log.Fatal(err) } } @@ -67,15 +67,15 @@ func del(c *cli.Context) { } for _, service := range c.Args() { - if _, ok := cfg.ServicesInclude[service]; !ok { + if _, ok := cfg.Rancher.ServicesInclude[service]; !ok { continue } - delete(cfg.ServicesInclude, service) + delete(cfg.Rancher.ServicesInclude, service) changed = true } if changed { - if err = cfg.Set("services_include", cfg.ServicesInclude); err != nil { + if err = cfg.Set("rancher.services_include", cfg.Rancher.ServicesInclude); err != nil { log.Fatal(err) } } @@ -89,20 +89,20 @@ func enable(c *cli.Context) { } for _, service := range c.Args() { - if val, ok := cfg.ServicesInclude[service]; !ok || !val { + if val, ok := cfg.Rancher.ServicesInclude[service]; !ok || !val { if strings.HasPrefix(service, "/") && !strings.HasPrefix(service, "/var/lib/rancher/conf") { log.Fatalf("ERROR: Service should be in path /var/lib/rancher/conf") } if _, err := docker.LoadServiceResource(service, true, cfg); err != nil { log.Fatalf("could not load service %s", service) } - cfg.ServicesInclude[service] = true + cfg.Rancher.ServicesInclude[service] = true changed = true } } if changed { - if err = cfg.Set("services_include", cfg.ServicesInclude); err != nil { + if err := cfg.Set("rancher.services_include", cfg.Rancher.ServicesInclude); err != nil { log.Fatal(err) } } @@ -115,11 +115,11 @@ func list(c *cli.Context) { } clone := make(map[string]bool) - for service, enabled := range cfg.ServicesInclude { + for service, enabled := range cfg.Rancher.ServicesInclude { clone[service] = enabled } - services, err := util.GetServices(cfg.Repositories.ToArray()) + services, err := util.GetServices(cfg.Rancher.Repositories.ToArray()) if err != nil { log.Fatalf("Failed to get services: %v", err) } diff --git a/cmd/control/tlsconf.go b/cmd/control/tlsconf.go index bdaaac44..b23e8b38 100644 --- a/cmd/control/tlsconf.go +++ b/cmd/control/tlsconf.go @@ -44,12 +44,12 @@ func tlsConfCommands() []cli.Command { } } -func writeCerts(generateServer bool, hostname []string, cfg *config.Config, certPath, keyPath, caCertPath, caKeyPath string) error { +func writeCerts(generateServer bool, hostname []string, cfg *config.CloudConfig, certPath, keyPath, caCertPath, caKeyPath string) error { if !generateServer { return machineUtil.GenerateCert([]string{""}, certPath, keyPath, caCertPath, caKeyPath, NAME, BITS) } - if cfg.UserDocker.ServerKey == "" || cfg.UserDocker.ServerCert == "" { + if cfg.Rancher.UserDocker.ServerKey == "" || cfg.Rancher.UserDocker.ServerCert == "" { err := machineUtil.GenerateCert(hostname, certPath, keyPath, caCertPath, caKeyPath, NAME, BITS) if err != nil { return err @@ -65,26 +65,28 @@ func writeCerts(generateServer bool, hostname []string, cfg *config.Config, cert return err } - return cfg.SetConfig(&config.Config{ - UserDocker: config.DockerConfig{ - CAKey: cfg.UserDocker.CAKey, - CACert: cfg.UserDocker.CACert, - ServerCert: string(cert), - ServerKey: string(key), + return cfg.SetConfig(&config.CloudConfig{ + Rancher: config.RancherConfig{ + UserDocker: config.DockerConfig{ + CAKey: cfg.Rancher.UserDocker.CAKey, + CACert: cfg.Rancher.UserDocker.CACert, + ServerCert: string(cert), + ServerKey: string(key), + }, }, }) } - if err := ioutil.WriteFile(certPath, []byte(cfg.UserDocker.ServerCert), 0400); err != nil { + if err := ioutil.WriteFile(certPath, []byte(cfg.Rancher.UserDocker.ServerCert), 0400); err != nil { return err } - return ioutil.WriteFile(keyPath, []byte(cfg.UserDocker.ServerKey), 0400) + return ioutil.WriteFile(keyPath, []byte(cfg.Rancher.UserDocker.ServerKey), 0400) } -func writeCaCerts(cfg *config.Config, caCertPath, caKeyPath string) error { - if cfg.UserDocker.CACert == "" { +func writeCaCerts(cfg *config.CloudConfig, caCertPath, caKeyPath string) error { + if cfg.Rancher.UserDocker.CACert == "" { if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, NAME, BITS); err != nil { return err } @@ -99,10 +101,12 @@ func writeCaCerts(cfg *config.Config, caCertPath, caKeyPath string) error { return err } - err = cfg.SetConfig(&config.Config{ - UserDocker: config.DockerConfig{ - CAKey: string(caKey), - CACert: string(caCert), + err = cfg.SetConfig(&config.CloudConfig{ + Rancher: config.RancherConfig{ + UserDocker: config.DockerConfig{ + CAKey: string(caKey), + CACert: string(caCert), + }, }, }) if err != nil { @@ -112,11 +116,11 @@ func writeCaCerts(cfg *config.Config, caCertPath, caKeyPath string) error { return nil } - if err := ioutil.WriteFile(caCertPath, []byte(cfg.UserDocker.CACert), 0400); err != nil { + if err := ioutil.WriteFile(caCertPath, []byte(cfg.Rancher.UserDocker.CACert), 0400); err != nil { return err } - return ioutil.WriteFile(caKeyPath, []byte(cfg.UserDocker.CAKey), 0400) + return ioutil.WriteFile(caKeyPath, []byte(cfg.Rancher.UserDocker.CAKey), 0400) } func tlsConfCreate(c *cli.Context) { diff --git a/cmd/network/network.go b/cmd/network/network.go index 8ec161d6..36f15765 100644 --- a/cmd/network/network.go +++ b/cmd/network/network.go @@ -20,14 +20,14 @@ import ( func Main() { args := os.Args if len(args) > 1 { - fmt.Println("call " + args[0] + " to load network config from rancher.yml config file") + fmt.Println("call " + args[0] + " to load network config from cloud-config.yml") return } cfg, err := config.LoadConfig() if err != nil { log.Fatal(err) } - ApplyNetworkConfigs(&cfg.Network) + ApplyNetworkConfigs(&cfg.Rancher.Network) } func createInterfaces(netCfg *config.NetworkConfig) error { diff --git a/cmd/respawn/respawn.go b/cmd/respawn/respawn.go index c498d249..4902a84c 100644 --- a/cmd/respawn/respawn.go +++ b/cmd/respawn/respawn.go @@ -68,7 +68,7 @@ func run(c *cli.Context) { var wg sync.WaitGroup for _, line := range strings.Split(string(input), "\n") { - if strings.TrimSpace(line) == "" { + if strings.TrimSpace(line) == "" || strings.Index(strings.TrimSpace(line), "#") == 0 { continue } wg.Add(1) diff --git a/config/config.go b/config/config.go index 57ef34ec..145de3fe 100644 --- a/config/config.go +++ b/config/config.go @@ -2,58 +2,21 @@ package config import ( "io/ioutil" - "os" "strings" - "github.com/rancherio/rancher-compose/librcompose/project" - log "github.com/Sirupsen/logrus" "github.com/rancherio/os/util" + "github.com/rancherio/rancher-compose/librcompose/project" "gopkg.in/yaml.v2" ) -func (c *Config) privilegedMerge(newConfig Config) error { - err := c.overlay(newConfig) - if err != nil { - return err - } - - for k, v := range newConfig.SystemContainers { - c.SystemContainers[k] = v - } - - return nil -} - -func (c *Config) overlay(newConfig Config) error { - newConfig.clearReadOnly() - return util.Convert(&newConfig, c) -} - -func (c *Config) clearReadOnly() { - c.BootstrapContainers = make(map[string]*project.ServiceConfig, 0) - c.SystemContainers = make(map[string]*project.ServiceConfig, 0) -} - -func clearReadOnly(data map[interface{}]interface{}) map[interface{}]interface{} { - newData := make(map[interface{}]interface{}) - for k, v := range data { - newData[k] = v - } - - delete(newData, "system_container") - delete(newData, "bootstrap_container") - - return newData -} - -func (c *Config) Import(bytes []byte) error { +func (c *CloudConfig) Import(bytes []byte) error { data, err := readConfig(bytes, PrivateConfigFile) if err != nil { return err } - if err = saveToDisk(data); err != nil { + if err := saveToDisk(data); err != nil { return err } @@ -61,7 +24,7 @@ func (c *Config) Import(bytes []byte) error { } // This function only sets "non-empty" values -func (c *Config) SetConfig(newConfig *Config) error { +func (c *CloudConfig) SetConfig(newConfig *CloudConfig) error { bytes, err := yaml.Marshal(newConfig) if err != nil { return err @@ -70,83 +33,63 @@ func (c *Config) SetConfig(newConfig *Config) error { return c.Merge(bytes) } -func (c *Config) Merge(bytes []byte) error { - data, err := readSavedConfig(bytes) +func (c *CloudConfig) Merge(bytes []byte) error { + data, err := readConfig(bytes, LocalConfigFile, PrivateConfigFile) if err != nil { return err } - err = saveToDisk(data) - if err != nil { + if err := saveToDisk(data); err != nil { return err } return c.Reload() } -func LoadConfig() (*Config, error) { +func LoadConfig() (*CloudConfig, error) { cfg := NewConfig() if err := cfg.Reload(); err != nil { + log.WithFields(log.Fields{"cfg": cfg}).Panicln(err) return nil, err } - if cfg.Debug { + if cfg.Rancher.Debug { log.SetLevel(log.DebugLevel) - if !util.Contains(cfg.UserDocker.Args, "-D") { - cfg.UserDocker.Args = append(cfg.UserDocker.Args, "-D") + if !util.Contains(cfg.Rancher.UserDocker.Args, "-D") { + cfg.Rancher.UserDocker.Args = append(cfg.Rancher.UserDocker.Args, "-D") } - if !util.Contains(cfg.SystemDocker.Args, "-D") { - cfg.SystemDocker.Args = append(cfg.SystemDocker.Args, "-D") + if !util.Contains(cfg.Rancher.SystemDocker.Args, "-D") { + cfg.Rancher.SystemDocker.Args = append(cfg.Rancher.SystemDocker.Args, "-D") } } return cfg, nil } -func (c *Config) readArgs() error { - log.Debug("Reading config args") - parts := make([]string, len(os.Args)) - - for _, arg := range os.Args[1:] { - if strings.HasPrefix(arg, "--") { - arg = arg[2:] - } - - kv := strings.SplitN(arg, "=", 2) - kv[0] = strings.Replace(kv[0], "-", ".", -1) - parts = append(parts, strings.Join(kv, "=")) - } - - cmdLine := strings.Join(parts, " ") - if len(cmdLine) == 0 { - return nil - } - - log.Debugf("Config Args %s", cmdLine) - - cmdLineObj := parseCmdline(strings.TrimSpace(cmdLine)) - - return c.merge(cmdLineObj) -} - -func (c *Config) merge(values map[interface{}]interface{}) error { - values = clearReadOnly(values) +func (c *CloudConfig) merge(values map[interface{}]interface{}) error { return util.Convert(values, c) } -func (c *Config) readFiles() error { - data, err := readSavedConfig(nil) +func (c *CloudConfig) readFiles() error { + data, err := readConfig(nil, CloudConfigFile, LocalConfigFile, PrivateConfigFile) if err != nil { + log.Panicln(err) return err } - return c.merge(data) + if err := c.merge(data); err != nil { + log.WithFields(log.Fields{"cfg": c, "data": data}).Panicln(err) + return err + } + + return nil } -func (c *Config) readCmdline() error { +func (c *CloudConfig) readCmdline() error { log.Debug("Reading config cmdline") cmdLine, err := ioutil.ReadFile("/proc/cmdline") if err != nil { + log.Panicln(err) return err } @@ -157,16 +100,21 @@ func (c *Config) readCmdline() error { log.Debugf("Config cmdline %s", cmdLine) cmdLineObj := parseCmdline(strings.TrimSpace(string(cmdLine))) - return c.merge(cmdLineObj) + + if err := c.merge(cmdLineObj); err != nil { + log.WithFields(log.Fields{"cfg": c, "cmdLine": cmdLine, "data": cmdLineObj}).Panicln(err) + return err + } + return nil } func Dump(private, full bool) (string, error) { - files := []string{CloudConfigFile, ConfigFile} + files := []string{CloudConfigFile, LocalConfigFile} if private { files = append(files, PrivateConfigFile) } - c := &Config{} + c := &CloudConfig{} if full { c = NewConfig() @@ -177,23 +125,23 @@ func Dump(private, full bool) (string, error) { return "", err } - err = c.merge(data) - if err != nil { + if err := c.merge(data); err != nil { return "", err } - err = c.readGlobals() - if err != nil { + if err := c.readGlobals(); err != nil { return "", err } + c.amendNils() + bytes, err := yaml.Marshal(c) return string(bytes), err } -func (c *Config) configureConsole() error { - if console, ok := c.SystemContainers[CONSOLE_CONTAINER]; ok { - if c.Console.Persistent { +func (c *CloudConfig) configureConsole() error { + if console, ok := c.Rancher.Services[CONSOLE_CONTAINER]; ok { + if c.Rancher.Console.Persistent { console.Labels.MapParts()[REMOVE] = "false" } else { console.Labels.MapParts()[REMOVE] = "true" @@ -203,22 +151,41 @@ func (c *Config) configureConsole() error { return nil } -func (c *Config) readGlobals() error { +func (c *CloudConfig) amendNils() error { + if c.Rancher.Environment == nil { + c.Rancher.Environment = map[string]string{} + } + if c.Rancher.Autoformat == nil { + c.Rancher.Autoformat = map[string]*project.ServiceConfig{} + } + if c.Rancher.BootstrapContainers == nil { + c.Rancher.BootstrapContainers = map[string]*project.ServiceConfig{} + } + if c.Rancher.Services == nil { + c.Rancher.Services = map[string]*project.ServiceConfig{} + } + if c.Rancher.ServicesInclude == nil { + c.Rancher.ServicesInclude = map[string]bool{} + } + return nil +} + +func (c *CloudConfig) readGlobals() error { return util.ShortCircuit( c.readCmdline, - c.readArgs, - c.configureConsole, + c.configureConsole, // TODO: this smells (it is a write hidden inside a read) ) } -func (c *Config) Reload() error { +func (c *CloudConfig) Reload() error { return util.ShortCircuit( c.readFiles, c.readGlobals, + c.amendNils, ) } -func (c *Config) Get(key string) (interface{}, error) { +func (c *CloudConfig) Get(key string) (interface{}, error) { data := make(map[interface{}]interface{}) err := util.Convert(c, &data) if err != nil { @@ -228,8 +195,8 @@ func (c *Config) Get(key string) (interface{}, error) { return getOrSetVal(key, data, nil), nil } -func (c *Config) Set(key string, value interface{}) error { - data, err := readSavedConfig(nil) +func (c *CloudConfig) Set(key string, value interface{}) error { + data, err := readConfig(nil, LocalConfigFile, PrivateConfigFile) if err != nil { return err } @@ -242,8 +209,7 @@ func (c *Config) Set(key string, value interface{}) error { return err } - err = saveToDisk(data) - if err != nil { + if err := saveToDisk(data); err != nil { return err } diff --git a/config/config_test.go b/config/config_test.go index 2a16b02e..0937e3cb 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -3,102 +3,252 @@ package config import ( "fmt" "gopkg.in/yaml.v2" - "log" "testing" "github.com/rancherio/os/util" + "github.com/stretchr/testify/require" + "strings" ) -import "reflect" -func testParseCmdline(t *testing.T) { - expected := map[string]interface{}{ - "rescue": true, - "key1": "value1", - "key2": "value2", - "keyArray": []string{"1", "2"}, - "obj1": map[string]interface{}{ - "key3": "3value", - "obj2": map[string]interface{}{ - "key4": true, +func TestNilMap(t *testing.T) { + assert := require.New(t) + var m map[string]interface{} = nil + assert.True(m == nil) +} + +func TestMapCopy(t *testing.T) { + assert := require.New(t) + m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} + m1 := util.MapCopy(m0) + + delete(m0, "a") + assert.Equal(len(m1), len(m0)+1) + + b0 := m0["b"].(map[interface{}]interface{}) + b1 := m1["b"].(map[interface{}]interface{}) + b1["e"] = "queer" + + assert.Equal(len(b1), len(b0)+1) +} + +func TestMapsIntersection(t *testing.T) { + assert := require.New(t) + + m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} + m1 := util.MapCopy(m0) + + delete(m0, "a") + b1 := m1["b"].(map[interface{}]interface{}) + delete(b1, "c") + expected := map[interface{}]interface{}{"b": map[interface{}]interface{}{}, "d": "4"} + assert.Equal(expected, util.MapsIntersection(m0, m1, util.Equal)) +} + +func TestMapsUnion(t *testing.T) { + assert := require.New(t) + + m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} + m1 := util.MapCopy(m0) + m1["e"] = "added" + m1["d"] = "replaced" + + delete(m0, "a") + b1 := m1["b"].(map[interface{}]interface{}) + delete(b1, "c") + expected := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "replaced", "e": "added"} + assert.Equal(expected, util.MapsUnion(m0, m1, util.Replace)) +} + +func TestFilterKey(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", + }, + }, + "user_docker": map[interface{}]interface{}{ + "ca_key": "ca_key-test3", + "ca_cert": "ca_cert-test4", + "args": []string{"args_test5"}, }, }, - "key5": 5, + } + expectedFiltered := map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "ssh": map[interface{}]interface{}{ + "keys": map[interface{}]interface{}{ + "dsa": "dsa-test1", + "dsa-pub": "dsa-test2", + }, + }, + }, + } + expectedRest := map[interface{}]interface{}{ + "ssh_authorized_keys": []string{"pubk1", "pubk2"}, + "hostname": "ros-test", + "rancher": map[interface{}]interface{}{ + "user_docker": map[interface{}]interface{}{ + "ca_key": "ca_key-test3", + "ca_cert": "ca_cert-test4", + "args": []string{"args_test5"}, + }, + }, + } + filtered, rest := filterKey(data, []string{"rancher", "ssh"}) + assert.Equal(expectedFiltered, filtered) + assert.Equal(expectedRest, rest) +} + +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", + }, + }, + "user_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{}{ + "user_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 TestParseCmdline(t *testing.T) { + assert := require.New(t) + + expected := map[interface{}]interface{}{ + "rancher": map[interface{}]interface{}{ + "rescue": true, + "key1": "value1", + "key2": "value2", + "keyArray": []string{"1", "2"}, + "obj1": map[interface{}]interface{}{ + "key3": "3value", + "obj2": map[interface{}]interface{}{ + "key4": true, + }, + }, + "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 := reflect.DeepEqual(actual, expected) - if !ok { - t.Fatalf("%v != %v", actual, expected) - } + assert.Equal(expected, actual) } func TestGet(t *testing.T) { + assert := require.New(t) + data := map[interface{}]interface{}{ "key": "value", - "key2": map[interface{}]interface{}{ - "subkey": "subvalue", - "subnum": 42, + "rancher": map[interface{}]interface{}{ + "key2": map[interface{}]interface{}{ + "subkey": "subvalue", + "subnum": 42, + }, }, } tests := map[string]interface{}{ - "key": "value", - "key2.subkey": "subvalue", - "key2.subnum": 42, - "key2.subkey2": "", - "foo": "", + "key": "value", + "rancher.key2.subkey": "subvalue", + "rancher.key2.subnum": 42, + "rancher.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) - } + assert.Equal(v, getOrSetVal(k, data, nil)) } } func TestSet(t *testing.T) { + assert := require.New(t) + data := map[interface{}]interface{}{ "key": "value", - "key2": map[interface{}]interface{}{ - "subkey": "subvalue", - "subnum": 42, + "rancher": map[interface{}]interface{}{ + "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, + "rancher": map[interface{}]interface{}{ + "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", + "key": "value2", + "rancher.key2.subkey": "subvalue2", + "rancher.key2.subkey2": "value", + "rancher.key2.subkey3": 43, + "rancher.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) - } + assert.Equal(v, getOrSetVal(k, data, nil)) } - if !reflect.DeepEqual(data, expected) { - t.Fatalf("Expected %v, got %v", expected, data) - } + assert.Equal(expected, data) } type OuterData struct { @@ -149,43 +299,35 @@ one: } func TestUserDocker(t *testing.T) { - config := &Config{ - UserDocker: DockerConfig{ - TLS: true, + assert := require.New(t) + + config := &CloudConfig{ + Rancher: RancherConfig{ + UserDocker: DockerConfig{ + TLS: true, + }, }, } bytes, err := yaml.Marshal(config) - if err != nil { - log.Fatal(err) - } + assert.Nil(err) config = NewConfig() err = yaml.Unmarshal(bytes, config) - if err != nil { - log.Fatal(err) - } + assert.Nil(err) - data := make(map[interface{}]interface{}) + data := make(map[interface{}]map[interface{}]interface{}) util.Convert(config, data) fmt.Println(data) - val, ok := data["user_docker"] - if !ok { - t.Fatal("Failed to find user_docker") - } + val, ok := data["rancher"]["user_docker"] + assert.True(ok) - if m, ok := val.(map[interface{}]interface{}); ok { - if v, ok := m["tls"]; ok { - if v != true { - t.Fatal("user_docker.tls is not true") - } - } else { - t.Fatal("user_docker.tls is not found") - } - } else { - t.Fatal("Bad data") - } + m, ok := val.(map[interface{}]interface{}) + assert.True(ok) + v, ok := m["tls"] + assert.True(ok) + assert.True(v.(bool)) } diff --git a/config/data_funcs.go b/config/data_funcs.go index 016eaaa2..467ceed9 100644 --- a/config/data_funcs.go +++ b/config/data_funcs.go @@ -3,11 +3,60 @@ package config import ( log "github.com/Sirupsen/logrus" + "github.com/rancherio/os/util" "regexp" "strconv" "strings" ) +func filterKey(data map[interface{}]interface{}, key []string) (filtered, rest map[interface{}]interface{}) { + if len(key) == 0 { + return data, map[interface{}]interface{}{} + } + + filtered = map[interface{}]interface{}{} + rest = util.MapCopy(data) + + k := key[0] + if d, ok := data[k]; ok { + switch d := d.(type) { + + case map[interface{}]interface{}: + f, r := filterKey(d, key[1:]) + + if len(f) != 0 { + filtered[k] = f + } + + if len(r) != 0 { + rest[k] = r + } else { + delete(rest, k) + } + + default: + filtered[k] = d + delete(rest, k) + } + + } + + return +} + +func filterDottedKeys(data map[interface{}]interface{}, keys []string) (filtered, rest map[interface{}]interface{}) { + filtered = map[interface{}]interface{}{} + rest = util.MapCopy(data) + + for _, key := range keys { + f, r := filterKey(data, strings.Split(key, ".")) + filtered = util.MapsUnion(filtered, f, util.Replace) + rest = util.MapsIntersection(rest, r, util.Equal) + } + + return +} + func getOrSetVal(args string, data map[interface{}]interface{}, value interface{}) interface{} { parts := strings.Split(args, ".") @@ -95,7 +144,7 @@ outer: } current := result - keys := strings.Split(kv[0], ".")[1:] + keys := strings.Split(kv[0], ".") for i, key := range keys { if i == len(keys)-1 { current[key] = DummyMarshall(value) diff --git a/config/default.go b/config/default.go index fa60b998..cb25de72 100644 --- a/config/default.go +++ b/config/default.go @@ -1,13 +1,14 @@ package config -func NewConfig() *Config { +func NewConfig() *CloudConfig { return ReadConfig(OsConfigFile) } -func ReadConfig(file string) *Config { +func ReadConfig(file string) *CloudConfig { if data, err := readConfig(nil, file); err == nil { - c := &Config{} + c := &CloudConfig{} c.merge(data) + c.amendNils() return c } else { return nil diff --git a/config/disk.go b/config/disk.go index c0d34541..d6eeb24c 100644 --- a/config/disk.go +++ b/config/disk.go @@ -18,38 +18,15 @@ func writeToFile(data interface{}, filename string) error { } func saveToDisk(data map[interface{}]interface{}) error { - config := make(map[interface{}]interface{}) - private := make(map[interface{}]interface{}) + private, config := filterDottedKeys(data, []string{ + "rancher.ssh", + "rancher.user_docker.ca_key", + "rancher.user_docker.ca_cert", + "rancher.user_docker.server_key", + "rancher.user_docker.server_cert", + }) - for k, v := range data { - if k == "ssh" { - private[k] = v - } else if k == "user_docker" { - var userDockerConfig DockerConfig - var userDockerConfigPrivate DockerConfig - err := util.Convert(v, &userDockerConfig) - if err != nil { - return err - } - - userDockerConfigPrivate.CAKey = userDockerConfig.CAKey - userDockerConfigPrivate.CACert = userDockerConfig.CACert - userDockerConfigPrivate.ServerKey = userDockerConfig.ServerKey - userDockerConfigPrivate.ServerCert = userDockerConfig.ServerCert - - userDockerConfig.CAKey = "" - userDockerConfig.CACert = "" - userDockerConfig.ServerKey = "" - userDockerConfig.ServerCert = "" - - config[k] = userDockerConfig - private[k] = userDockerConfigPrivate - } else { - config[k] = v - } - } - - err := writeToFile(config, ConfigFile) + err := writeToFile(config, LocalConfigFile) if err != nil { return err } @@ -57,10 +34,6 @@ func saveToDisk(data map[interface{}]interface{}) error { return writeToFile(private, PrivateConfigFile) } -func readSavedConfig(bytes []byte) (map[interface{}]interface{}, error) { - return readConfig(bytes, CloudConfigFile, ConfigFile, PrivateConfigFile) -} - func readConfig(bytes []byte, files ...string) (map[interface{}]interface{}, error) { // 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. @@ -77,7 +50,7 @@ func readConfig(bytes []byte, files ...string) (map[interface{}]interface{}, err return nil, err } - util.MergeMaps(left, right) + left = util.MapsUnion(left, right, util.Replace) } if bytes != nil && len(bytes) > 0 { @@ -86,7 +59,7 @@ func readConfig(bytes []byte, files ...string) (map[interface{}]interface{}, err return nil, err } - util.MergeMaps(left, right) + left = util.MapsUnion(left, right, util.Replace) } return left, nil diff --git a/config/init.go b/config/init.go index c24c546e..7cf6bbbf 100644 --- a/config/init.go +++ b/config/init.go @@ -6,9 +6,9 @@ import ( log "github.com/Sirupsen/logrus" ) -type InitFunc func(*Config) error +type InitFunc func(*CloudConfig) error -func RunInitFuncs(cfg *Config, initFuncs []InitFunc) error { +func RunInitFuncs(cfg *CloudConfig, initFuncs []InitFunc) error { for i, initFunc := range initFuncs { log.Debugf("[%d/%d] Starting", i+1, len(initFuncs)) if err := initFunc(cfg); err != nil { diff --git a/config/types.go b/config/types.go index 0338bc47..226468dc 100644 --- a/config/types.go +++ b/config/types.go @@ -1,6 +1,9 @@ package config -import "github.com/rancherio/rancher-compose/librcompose/project" +import ( + "github.com/coreos/coreos-cloudinit/config" + "github.com/rancherio/rancher-compose/librcompose/project" +) const ( CONSOLE_CONTAINER = "console" @@ -24,14 +27,17 @@ const ( RELOAD_CONFIG = "io.rancher.os.reloadconfig" SCOPE = "io.rancher.os.scope" SYSTEM = "system" + + OsConfigFile = "/os-config.yml" + CloudConfigFile = "/var/lib/rancher/conf/cloud-config.yml" + CloudConfigScriptFile = "/var/lib/rancher/conf/cloud-config-script" + MetaDataFile = "/var/lib/rancher/conf/metadata" + LocalConfigFile = "/var/lib/rancher/conf/cloud-config-local.yml" + PrivateConfigFile = "/var/lib/rancher/conf/cloud-config-private.yml" ) var ( - VERSION string - OsConfigFile = "/os-config.yml" - CloudConfigFile = "/var/lib/rancher/conf/cloud-config-rancher.yml" - ConfigFile = "/var/lib/rancher/conf/rancher.yml" - PrivateConfigFile = "/var/lib/rancher/conf/rancher-private.yml" + VERSION string ) type ContainerConfig struct { @@ -49,7 +55,16 @@ type Repository struct { type Repositories map[string]Repository -type Config struct { +type CloudConfig struct { + SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"` + WriteFiles []config.File `yaml:"write_files"` + Hostname string `yaml:"hostname"` + Users []config.User `yaml:"users"` + + Rancher RancherConfig `yaml:"rancher,omitempty"` +} + +type RancherConfig struct { Environment map[string]string `yaml:"environment,omitempty"` Services map[string]*project.ServiceConfig `yaml:"services,omitempty"` BootstrapContainers map[string]*project.ServiceConfig `yaml:"bootstrap,omitempty"` @@ -65,7 +80,6 @@ type Config struct { Repositories Repositories `yaml:"repositories,omitempty"` Ssh SshConfig `yaml:"ssh,omitempty"` State StateConfig `yaml:"state,omitempty"` - SystemContainers map[string]*project.ServiceConfig `yaml:"system_containers,omitempty"` SystemDocker DockerConfig `yaml:"system_docker,omitempty"` Upgrade UpgradeConfig `yaml:"upgrade,omitempty"` UserContainers []ContainerConfig `yaml:"user_containers,omitempty"` diff --git a/docker/container.go b/docker/container.go index 74553f6e..3f244446 100644 --- a/docker/container.go +++ b/docker/container.go @@ -282,12 +282,10 @@ func (c *Container) addLink(link string) { func (c *Container) parseService() { if c.requiresSyslog() { c.addLink("syslog") - log.Infof("[%v]: Implicitly linked to 'syslog'", c.Name) } if c.requiresUserDocker() { c.addLink("dockerwait") - log.Infof("[%v]: Implicitly linked to 'dockerwait'", c.Name) } else if c.ContainerCfg.Service.Image != "" { client, err := NewClient(c.dockerHost) if err != nil { @@ -298,7 +296,6 @@ func (c *Container) parseService() { i, _ := client.InspectImage(c.ContainerCfg.Service.Image) if i == nil { c.addLink("network") - log.Infof("[%v]: Implicitly linked to 'network'", c.Name) } } @@ -538,6 +535,7 @@ func appendVolumesFrom(client *dockerClient.Client, containerCfg *config.Contain } func (c *Container) start(createOnly, wait bool) *Container { + log.Debugf("Container: STARTING '%v', createOnly: %v, !detach: %v, wait: %v", c.Name, createOnly, !c.detach, wait) c.Lookup() c.Stage() @@ -619,9 +617,11 @@ func (c *Container) start(createOnly, wait bool) *Container { } } + log.Debugf("Container: WAIT? '%v' !c.detach && wait: %v", c.Name, !c.detach && wait) if !c.detach && wait { var exitCode int exitCode, c.Err = client.WaitContainer(c.Container.ID) + log.Debugf("Container: FINISHED '%v', exitCode: %v", c.Name, exitCode) if exitCode != 0 { c.Err = errors.New(fmt.Sprintf("Container %s exited with code %d", c.Name, exitCode)) } diff --git a/docker/factory.go b/docker/factory.go index dd10fea1..5d394fd0 100644 --- a/docker/factory.go +++ b/docker/factory.go @@ -9,7 +9,7 @@ import ( ) type ContainerFactory struct { - cfg *config.Config + cfg *config.CloudConfig } type containerBasedService struct { @@ -18,10 +18,10 @@ type containerBasedService struct { project *project.Project container *Container serviceConfig *project.ServiceConfig - cfg *config.Config + cfg *config.CloudConfig } -func NewContainerFactory(cfg *config.Config) *ContainerFactory { +func NewContainerFactory(cfg *config.CloudConfig) *ContainerFactory { return &ContainerFactory{ cfg: cfg, } @@ -34,7 +34,7 @@ func (c *containerBasedService) Up() error { fakeCreate := false create := containerCfg.CreateOnly - if util.Contains(c.cfg.Disable, c.name) { + if util.Contains(c.cfg.Rancher.Disable, c.name) { fakeCreate = true } diff --git a/docker/services.go b/docker/services.go index 6a7efda1..babeebac 100644 --- a/docker/services.go +++ b/docker/services.go @@ -11,7 +11,7 @@ import ( ) type configEnvironment struct { - cfg *config.Config + cfg *config.CloudConfig } func appendEnv(array []string, key, value string) []string { @@ -23,11 +23,11 @@ func appendEnv(array []string, key, value string) []string { return append(array, fmt.Sprintf("%s=%s", key, value)) } -func lookupKeys(cfg *config.Config, keys ...string) []string { +func lookupKeys(cfg *config.CloudConfig, keys ...string) []string { for _, key := range keys { if strings.HasSuffix(key, "*") { result := []string{} - for envKey, envValue := range cfg.Environment { + for envKey, envValue := range cfg.Rancher.Environment { keyPrefix := key[:len(key)-1] if strings.HasPrefix(envKey, keyPrefix) { result = appendEnv(result, envKey, envValue) @@ -37,7 +37,7 @@ func lookupKeys(cfg *config.Config, keys ...string) []string { if len(result) > 0 { return result } - } else if value, ok := cfg.Environment[key]; ok { + } else if value, ok := cfg.Rancher.Environment[key]; ok { return appendEnv([]string{}, key, value) } } @@ -50,7 +50,7 @@ func (c *configEnvironment) Lookup(key, serviceName string, serviceConfig *proje return lookupKeys(c.cfg, fullKey, key) } -func RunServices(name string, cfg *config.Config, configs map[string]*project.ServiceConfig) error { +func RunServices(name string, cfg *config.CloudConfig, configs map[string]*project.ServiceConfig) error { network := false projectEvents := make(chan project.ProjectEvent) p := project.NewProject(name, NewContainerFactory(cfg)) @@ -61,21 +61,26 @@ func RunServices(name string, cfg *config.Config, configs map[string]*project.Se for name, serviceConfig := range configs { if err := p.AddConfig(name, serviceConfig); err != nil { log.Infof("Failed loading service %s", name) + continue } + enabled[name] = true } p.ReloadCallback = func() error { - err := cfg.Reload() - if err != nil { + if p.Name != "system-init" { + return nil + } + + if err := cfg.Reload(); err != nil { return err } - for service, serviceEnabled := range cfg.ServicesInclude { + for service, serviceEnabled := range cfg.Rancher.ServicesInclude { if !serviceEnabled { continue } - if _, ok := enabled[service]; ok { + if en, ok := enabled[service]; ok && en { continue } @@ -89,8 +94,7 @@ func RunServices(name string, cfg *config.Config, configs map[string]*project.Se continue } - err = p.Load(bytes) - if err != nil { + if err := p.Load(bytes); err != nil { log.Errorf("Failed to load %s : %v", service, err) continue } @@ -98,17 +102,15 @@ func RunServices(name string, cfg *config.Config, configs map[string]*project.Se enabled[service] = true } - for service, config := range cfg.Services { - if _, ok := enabled[service]; ok { + for service, config := range cfg.Rancher.Services { + if en, ok := enabled[service]; ok && en { continue } - err = p.AddConfig(service, config) - if err != nil { + if err := p.AddConfig(service, config); err != nil { log.Errorf("Failed to load %s : %v", service, err) continue } - enabled[service] = true } @@ -123,14 +125,13 @@ func RunServices(name string, cfg *config.Config, configs map[string]*project.Se } }() - err := p.ReloadCallback() - if err != nil { + if err := p.ReloadCallback(); err != nil { log.Errorf("Failed to reload %s : %v", name, err) return err } return p.Up() } -func LoadServiceResource(name string, network bool, cfg *config.Config) ([]byte, error) { - return util.LoadResource(name, network, cfg.Repositories.ToArray()) +func LoadServiceResource(name string, network bool, cfg *config.CloudConfig) ([]byte, error) { + return util.LoadResource(name, network, cfg.Rancher.Repositories.ToArray()) } diff --git a/init/bootstrap.go b/init/bootstrap.go index 019c7f8d..1d64fa71 100644 --- a/init/bootstrap.go +++ b/init/bootstrap.go @@ -14,24 +14,24 @@ import ( "strings" ) -func autoformat(cfg *config.Config) error { - if len(cfg.State.Autoformat) == 0 || util.ResolveDevice(cfg.State.Dev) != "" { +func autoformat(cfg *config.CloudConfig) error { + if len(cfg.Rancher.State.Autoformat) == 0 || util.ResolveDevice(cfg.Rancher.State.Dev) != "" { return nil } - AUTOFORMAT := "AUTOFORMAT=" + strings.Join(cfg.State.Autoformat, " ") - FORMATZERO := "FORMATZERO=" + fmt.Sprint(cfg.State.FormatZero) - cfg.Autoformat["autoformat"].Environment = project.NewMaporEqualSlice([]string{AUTOFORMAT, FORMATZERO}) + AUTOFORMAT := "AUTOFORMAT=" + strings.Join(cfg.Rancher.State.Autoformat, " ") + FORMATZERO := "FORMATZERO=" + fmt.Sprint(cfg.Rancher.State.FormatZero) + cfg.Rancher.Autoformat["autoformat"].Environment = project.NewMaporEqualSlice([]string{AUTOFORMAT, FORMATZERO}) log.Info("Running Autoformat services") - err := docker.RunServices("autoformat", cfg, cfg.Autoformat) + err := docker.RunServices("autoformat", cfg, cfg.Rancher.Autoformat) return err } -func runBootstrapContainers(cfg *config.Config) error { +func runBootstrapContainers(cfg *config.CloudConfig) error { log.Info("Running Bootstrap services") - return docker.RunServices("bootstrap", cfg, cfg.BootstrapContainers) + return docker.RunServices("bootstrap", cfg, cfg.Rancher.BootstrapContainers) } -func startDocker(cfg *config.Config) (chan interface{}, error) { +func startDocker(cfg *config.CloudConfig) (chan interface{}, error) { for _, d := range []string{config.DOCKER_SYSTEM_HOST, "/var/run"} { err := os.MkdirAll(d, 0700) if err != nil { @@ -39,8 +39,8 @@ func startDocker(cfg *config.Config) (chan interface{}, error) { } } - cmd := exec.Command(cfg.BootstrapDocker.Args[0], cfg.BootstrapDocker.Args[1:]...) - if cfg.Debug { + cmd := exec.Command(cfg.Rancher.BootstrapDocker.Args[0], cfg.Rancher.BootstrapDocker.Args[1:]...) + if cfg.Rancher.Debug { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr } @@ -67,7 +67,7 @@ func stopDocker(c chan interface{}) error { return os.RemoveAll(config.DOCKER_SYSTEM_HOME) } -func bootstrap(cfg *config.Config) error { +func bootstrap(cfg *config.CloudConfig) error { log.Info("Launching Bootstrap Docker") c, err := startDocker(cfg) if err != nil { diff --git a/init/init.go b/init/init.go index bbefbeda..26dc4eec 100644 --- a/init/init.go +++ b/init/init.go @@ -72,7 +72,7 @@ var ( } ) -func createSymlinks(cfg *config.Config, symlinks map[string]string) error { +func createSymlinks(cfg *config.CloudConfig, symlinks map[string]string) error { log.Debug("Creating symlinking") for dest, src := range symlinks { if _, err := os.Stat(dest); os.IsNotExist(err) { @@ -112,12 +112,12 @@ func createMounts(mounts ...[]string) error { return nil } -func remountRo(cfg *config.Config) error { +func remountRo(cfg *config.CloudConfig) error { log.Info("Remouting root read only") return util.Remount("/", "ro") } -func mountCgroups(cfg *config.Config) error { +func mountCgroups(cfg *config.CloudConfig) error { for _, cgroup := range cgroups { err := createDirs("/sys/fs/cgroup/" + cgroup) if err != nil { @@ -137,7 +137,7 @@ func mountCgroups(cfg *config.Config) error { return nil } -func extractModules(cfg *config.Config) error { +func extractModules(cfg *config.CloudConfig) error { if _, err := os.Stat(config.MODULES_ARCHIVE); os.IsNotExist(err) { log.Debug("Modules do not exist") return nil @@ -147,7 +147,7 @@ func extractModules(cfg *config.Config) error { return util.ExtractTar(config.MODULES_ARCHIVE, "/") } -func setResolvConf(cfg *config.Config) error { +func setResolvConf(cfg *config.CloudConfig) error { log.Debug("Creating /etc/resolv.conf") //f, err := os.OpenFile("/etc/resolv.conf", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) f, err := os.Create("/etc/resolv.conf") @@ -157,14 +157,14 @@ func setResolvConf(cfg *config.Config) error { defer f.Close() - for _, dns := range cfg.Network.Dns.Nameservers { + for _, dns := range cfg.Rancher.Network.Dns.Nameservers { content := fmt.Sprintf("nameserver %s\n", dns) if _, err = f.Write([]byte(content)); err != nil { return err } } - search := strings.Join(cfg.Network.Dns.Search, " ") + search := strings.Join(cfg.Rancher.Network.Dns.Search, " ") if search != "" { content := fmt.Sprintf("search %s\n", search) if _, err = f.Write([]byte(content)); err != nil { @@ -172,8 +172,8 @@ func setResolvConf(cfg *config.Config) error { } } - if cfg.Network.Dns.Domain != "" { - content := fmt.Sprintf("domain %s\n", cfg.Network.Dns.Domain) + if cfg.Rancher.Network.Dns.Domain != "" { + content := fmt.Sprintf("domain %s\n", cfg.Rancher.Network.Dns.Domain) if _, err = f.Write([]byte(content)); err != nil { return err } @@ -182,7 +182,7 @@ func setResolvConf(cfg *config.Config) error { return nil } -func loadModules(cfg *config.Config) error { +func loadModules(cfg *config.CloudConfig) error { filesystems, err := ioutil.ReadFile("/proc/filesystems") if err != nil { return err @@ -196,7 +196,7 @@ func loadModules(cfg *config.Config) error { } } - for _, module := range cfg.Modules { + for _, module := range cfg.Rancher.Modules { log.Debugf("Loading module %s", module) err = exec.Command("/sbin/modprobe", module).Run() if err != nil { @@ -207,7 +207,7 @@ func loadModules(cfg *config.Config) error { return nil } -func sysInit(cfg *config.Config) error { +func sysInit(cfg *config.CloudConfig) error { args := append([]string{SYSINIT}, os.Args[1:]...) var cmd *exec.Cmd @@ -227,9 +227,9 @@ func sysInit(cfg *config.Config) error { return os.Stdin.Close() } -func execDocker(cfg *config.Config) error { +func execDocker(cfg *config.CloudConfig) error { log.Info("Launching System Docker") - if !cfg.Debug { + if !cfg.Rancher.Debug { output, err := os.Create("/var/log/system-docker.log") if err != nil { return err @@ -240,7 +240,7 @@ func execDocker(cfg *config.Config) error { } os.Stdin.Close() - return syscall.Exec(SYSTEM_DOCKER, cfg.SystemDocker.Args, os.Environ()) + return syscall.Exec(SYSTEM_DOCKER, cfg.Rancher.SystemDocker.Args, os.Environ()) } func MainInit() { @@ -249,24 +249,24 @@ func MainInit() { } } -func mountStateTmpfs(cfg *config.Config) error { +func mountStateTmpfs(cfg *config.CloudConfig) error { log.Debugf("State will not be persisted") return util.Mount("none", STATE, "tmpfs", "") } -func mountState(cfg *config.Config) error { +func mountState(cfg *config.CloudConfig) error { var err error - if cfg.State.Dev != "" { - dev := util.ResolveDevice(cfg.State.Dev) + if cfg.Rancher.State.Dev != "" { + dev := util.ResolveDevice(cfg.Rancher.State.Dev) if dev == "" { - msg := fmt.Sprintf("Could not resolve device %q", cfg.State.Dev) + msg := fmt.Sprintf("Could not resolve device %q", cfg.Rancher.State.Dev) log.Infof(msg) return fmt.Errorf(msg) } log.Infof("Mounting state device %s to %s", dev, STATE) - fsType := cfg.State.FsType + fsType := cfg.Rancher.State.FsType if fsType == "auto" { fsType, err = util.GetFsType(dev) } @@ -282,16 +282,16 @@ func mountState(cfg *config.Config) error { return err } -func tryMountAndBootstrap(cfg *config.Config) error { +func tryMountAndBootstrap(cfg *config.CloudConfig) error { if err := mountState(cfg); err != nil { if err := bootstrap(cfg); err != nil { - if cfg.State.Required { + if cfg.Rancher.State.Required { return err } return mountStateTmpfs(cfg) } if err := mountState(cfg); err != nil { - if cfg.State.Required { + if cfg.Rancher.State.Required { return err } return mountStateTmpfs(cfg) @@ -300,11 +300,11 @@ func tryMountAndBootstrap(cfg *config.Config) error { return nil } -func createGroups(cfg *config.Config) error { +func createGroups(cfg *config.CloudConfig) error { return ioutil.WriteFile("/etc/group", []byte("root:x:0:\n"), 0644) } -func touchSocket(cfg *config.Config) error { +func touchSocket(cfg *config.CloudConfig) error { for _, path := range []string{"/var/run/docker.sock", "/var/run/system-docker.sock"} { if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { return err @@ -318,8 +318,8 @@ func touchSocket(cfg *config.Config) error { return nil } -func setupSystemBridge(cfg *config.Config) error { - bridge, cidr := cfg.SystemDocker.BridgeConfig() +func setupSystemBridge(cfg *config.CloudConfig) error { + bridge, cidr := cfg.Rancher.SystemDocker.BridgeConfig() if bridge == "" { return nil } @@ -335,20 +335,20 @@ func setupSystemBridge(cfg *config.Config) error { } func RunInit() error { - var cfg config.Config + var cfg config.CloudConfig os.Setenv("PATH", "/sbin:/usr/sbin:/usr/bin") os.Setenv("DOCKER_RAMDISK", "true") initFuncs := []config.InitFunc{ - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { return createDirs(dirs...) }, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { log.Info("Setting up mounts") return createMounts(mounts...) }, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { newCfg, err := config.LoadConfig() if err == nil { newCfg, err = config.LoadConfig() @@ -357,17 +357,15 @@ func RunInit() error { *cfg = *newCfg } - if cfg.Debug { + if cfg.Rancher.Debug { cfgString, _ := config.Dump(false, true) - if cfgString != "" { - log.Debugf("Config: %s", cfgString) - } + log.Debugf("os-config dump: \n%s", cfgString) } return err }, mountCgroups, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { return createSymlinks(cfg, symlinks) }, createGroups, @@ -376,15 +374,15 @@ func RunInit() error { setResolvConf, setupSystemBridge, tryMountAndBootstrap, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { return cfg.Reload() }, loadModules, setResolvConf, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { return createDirs(postDirs...) }, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { return createMounts(postMounts...) }, touchSocket, diff --git a/init/sysinit.go b/init/sysinit.go index 087a9814..40af7591 100644 --- a/init/sysinit.go +++ b/init/sysinit.go @@ -11,24 +11,6 @@ import ( "github.com/rancherio/os/docker" ) -func importImage(client *dockerClient.Client, name, fileName string) error { - file, err := os.Open(fileName) - if err != nil { - return err - } - - defer file.Close() - - log.Debugf("Importing image for %s", fileName) - repo, tag := dockerClient.ParseRepositoryTag(name) - return client.ImportImage(dockerClient.ImportImageOptions{ - Source: "-", - Repository: repo, - Tag: tag, - InputStream: file, - }) -} - func hasImage(name string) bool { stamp := path.Join(STATE, name) if _, err := os.Stat(stamp); os.IsNotExist(err) { @@ -37,7 +19,7 @@ func hasImage(name string) bool { return true } -func findImages(cfg *config.Config) ([]string, error) { +func findImages(cfg *config.CloudConfig) ([]string, error) { log.Debugf("Looking for images at %s", config.IMAGES_PATH) result := []string{} @@ -68,7 +50,7 @@ func findImages(cfg *config.Config) ([]string, error) { return result, nil } -func loadImages(cfg *config.Config) error { +func loadImages(cfg *config.CloudConfig) error { images, err := findImages(cfg) if err != nil || len(images) == 0 { return err @@ -106,12 +88,12 @@ func loadImages(cfg *config.Config) error { return nil } -func runContainers(cfg *config.Config) error { - return docker.RunServices("system-init", cfg, cfg.SystemContainers) +func runContainers(cfg *config.CloudConfig) error { + return docker.RunServices("system-init", cfg, cfg.Rancher.Services) } -func tailConsole(cfg *config.Config) error { - if !cfg.Console.Tail { +func tailConsole(cfg *config.CloudConfig) error { + if !cfg.Rancher.Console.Tail { return nil } @@ -120,7 +102,7 @@ func tailConsole(cfg *config.Config) error { return err } - console, ok := cfg.SystemContainers[config.CONSOLE_CONTAINER] + console, ok := cfg.Rancher.Services[config.CONSOLE_CONTAINER] if !ok { log.Error("Console not found") return nil @@ -151,11 +133,11 @@ func SysInit() error { initFuncs := []config.InitFunc{ loadImages, runContainers, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { syscall.Sync() return nil }, - func(cfg *config.Config) error { + func(cfg *config.CloudConfig) error { log.Infof("RancherOS %s started", config.VERSION) return nil }, diff --git a/os-config.yml b/os-config.yml index ef7ba426..852a5efb 100644 --- a/os-config.yml +++ b/os-config.yml @@ -1,419 +1,310 @@ -bootstrap: - udev: - image: rancher/os-udev:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: host - uts: host - privileged: true - volumes: - - /dev:/host/dev - - /lib/modules:/lib/modules - - /lib/firmware:/lib/firmware -autoformat: +rancher: + bootstrap: + udev: + image: rancher/os-udev:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + log_driver: json-file + net: host + uts: host + privileged: true + volumes: + - /dev:/host/dev + - /lib/modules:/lib/modules + - /lib/firmware:/lib/firmware autoformat: - image: rancher/os-autoformat:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - volumes: [] - udev: - image: rancher/os-udev:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: - - autoformat - log_driver: json-file - net: host - uts: host - privileged: true - volumes: - - /dev:/host/dev - - /lib/modules:/lib/modules - - /lib/firmware:/lib/firmware -bootstrap_docker: - args: [docker, -d, -s, overlay, -b, none, --restart=false, -g, /var/lib/system-docker, - -G, root, -H, 'unix:///var/run/system-docker.sock'] -cloud_init: - datasources: - - configdrive:/media/config-2 -services_include: - ubuntu-console: false -network: - dns: - nameservers: [8.8.8.8, 8.8.4.4] - interfaces: - eth*: - dhcp: true - lo: - address: 127.0.0.1/8 -repositories: - core: - url: https://raw.githubusercontent.com/rancherio/os-services/v0.4.0 -state: - fstype: auto - dev: LABEL=RANCHER_STATE -system_containers: - acpid: - image: rancher/os-acpid:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.scope: system - links: [] - net: host - uts: host - privileged: true - volumes_from: - - command-volumes - - system-volumes - all-volumes: - image: rancher/os-state:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.createonly: true - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - read_only: true - volumes_from: - - docker-volumes - - command-volumes - - user-volumes - - system-volumes - cloud-init: - image: rancher/os-cloudinit:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.reloadconfig: true - io.rancher.os.scope: system - links: - - preload-user-images - - cloud-init-pre - - network - net: host - uts: host - privileged: true - volumes_from: - - command-volumes - - system-volumes - cloud-init-pre: - image: rancher/os-cloudinit:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: - - CLOUD_INIT_NETWORK=false - labels: - io.rancher.os.detach: false - io.rancher.os.reloadconfig: true - io.rancher.os.scope: system - links: - - preload-system-images - net: host - uts: host - privileged: true - volumes_from: - - command-volumes - - system-volumes - command-volumes: - image: rancher/os-state:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.createonly: true - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - read_only: true - volumes: - - /init:/sbin/halt:ro - - /init:/sbin/poweroff:ro - - /init:/sbin/reboot:ro - - /init:/sbin/shutdown:ro - - /init:/sbin/netconf:ro - - /init:/usr/bin/cloud-init:ro - - /init:/usr/bin/rancherctl:ro - - /init:/usr/bin/ros:ro - - /init:/usr/bin/respawn:ro - - /init:/usr/bin/system-docker:ro - - /init:/usr/sbin/wait-for-docker:ro - - /lib/modules:/lib/modules - - /usr/bin/docker:/usr/bin/docker:ro - console: - image: rancher/os-console:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.remove: true - io.rancher.os.scope: system - links: - - cloud-init - net: host - uts: host - pid: host - ipc: host - privileged: true - restart: always - volumes_from: - - all-volumes - docker: - image: rancher/os-docker:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.scope: system - links: - - network - net: host - uts: host - pid: host - ipc: host - privileged: true - restart: always - volumes_from: - - all-volumes - docker-volumes: - image: rancher/os-state:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.createonly: true - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - read_only: true - volumes: - - /var/lib/rancher/conf:/var/lib/rancher/conf - - /var/lib/docker:/var/lib/docker - - /var/lib/system-docker:/var/lib/system-docker - dockerwait: - image: rancher/os-dockerwait:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: - - docker - net: host - uts: host - volumes_from: - - all-volumes + autoformat: + image: rancher/os-autoformat:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + udev: + image: rancher/os-udev:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + links: + - autoformat + log_driver: json-file + net: host + uts: host + privileged: true + volumes: + - /dev:/host/dev + - /lib/modules:/lib/modules + - /lib/firmware:/lib/firmware + bootstrap_docker: + args: [docker, -d, -s, overlay, -b, none, --restart=false, -g, /var/lib/system-docker, + -G, root, -H, 'unix:///var/run/system-docker.sock'] + cloud_init: + datasources: + - configdrive:/media/config-2 + services_include: {} network: - image: rancher/os-network:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: - - cloud-init-pre - net: host - uts: host - privileged: true - volumes_from: - - command-volumes - - system-volumes - ntp: - image: rancher/os-ntp:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.scope: system - links: - - cloud-init - - network - net: host - uts: host - privileged: true - restart: always - preload-system-images: - image: rancher/os-preload:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: [] - privileged: true - volumes: - - /var/run/system-docker.sock:/var/run/docker.sock - - /var/lib/system-docker/preload:/mnt/preload - volumes_from: - - command-volumes - - system-volumes - preload-user-images: - image: rancher/os-preload:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.detach: false - io.rancher.os.scope: system - links: - - dockerwait - privileged: true - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /var/lib/docker/preload:/mnt/preload - volumes_from: - - command-volumes - - system-volumes - syslog: - image: rancher/os-syslog:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: host - uts: host - privileged: true - restart: always - volumes_from: - - system-volumes - system-volumes: - image: rancher/os-state:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.createonly: true - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - read_only: true - volumes: - - /dev:/host/dev - - /os-config.yml:/os-config.yml - - /var/lib/rancher:/var/lib/rancher - - /var/lib/rancher/conf:/var/lib/rancher/conf - - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt.rancher - - /lib/modules:/lib/modules - - /lib/firmware:/lib/firmware - - /var/run:/var/run - - /var/log:/var/log - udev: - image: rancher/os-udev:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: - - DAEMON=true - labels: - io.rancher.os.detach: true - io.rancher.os.scope: system - links: [] - net: host - uts: host - privileged: true - restart: always - volumes_from: - - system-volumes - user-volumes: - image: rancher/os-state:v0.4.0-dev - command: [] - dns: [] - dns_search: [] - env_file: [] - environment: [] - labels: - io.rancher.os.createonly: true - io.rancher.os.scope: system - links: [] - log_driver: json-file - net: none - privileged: true - read_only: true - volumes: - - /home:/home - - /opt:/opt -system_docker: - args: [docker, -d, --log-driver, syslog, -s, overlay, -b, docker-sys, --fixed-cidr, - 172.18.42.1/16, --restart=false, -g, /var/lib/system-docker, -G, root, - -H, 'unix:///var/run/system-docker.sock', --userland-proxy=false] -upgrade: - url: https://releases.rancher.com/os/versions.yml - image: rancher/os -user_docker: - tls_args: [--tlsverify, --tlscacert=ca.pem, --tlscert=server-cert.pem, --tlskey=server-key.pem, - '-H=0.0.0.0:2376'] - args: [docker, -d, -s, overlay, -G, docker, -H, 'unix:///var/run/docker.sock', --userland-proxy=false] + dns: + nameservers: [8.8.8.8, 8.8.4.4] + interfaces: + eth*: + dhcp: true + lo: + address: 127.0.0.1/8 + repositories: + core: + url: https://raw.githubusercontent.com/rancherio/os-services/v0.4.0 + state: + fstype: auto + dev: LABEL=RANCHER_STATE + services: + acpid: + image: rancher/os-acpid:v0.4.0-dev + labels: + io.rancher.os.scope: system + net: host + uts: host + privileged: true + volumes_from: + - command-volumes + - system-volumes + all-volumes: + image: rancher/os-state:v0.4.0-dev + labels: + io.rancher.os.createonly: true + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + read_only: true + volumes_from: + - docker-volumes + - command-volumes + - user-volumes + - system-volumes + cloud-init: + image: rancher/os-cloudinit:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.reloadconfig: true + io.rancher.os.scope: system + links: + - cloud-init-pre + - network + net: host + uts: host + privileged: true + volumes_from: + - command-volumes + - system-volumes + cloud-init-pre: + image: rancher/os-cloudinit:v0.4.0-dev + environment: + - CLOUD_INIT_NETWORK=false + labels: + io.rancher.os.detach: false + io.rancher.os.reloadconfig: true + io.rancher.os.scope: system + links: + - preload-system-images + net: host + uts: host + privileged: true + volumes_from: + - command-volumes + - system-volumes + command-volumes: + image: rancher/os-state:v0.4.0-dev + labels: + io.rancher.os.createonly: true + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + read_only: true + volumes: + - /init:/sbin/halt:ro + - /init:/sbin/poweroff:ro + - /init:/sbin/reboot:ro + - /init:/sbin/shutdown:ro + - /init:/sbin/netconf:ro + - /init:/usr/bin/cloud-init:ro + - /init:/usr/bin/rancherctl:ro + - /init:/usr/bin/ros:ro + - /init:/usr/bin/respawn:ro + - /init:/usr/bin/system-docker:ro + - /init:/usr/sbin/wait-for-docker:ro + - /lib/modules:/lib/modules + - /usr/bin/docker:/usr/bin/docker:ro + console: + image: rancher/os-console:v0.4.0-dev + labels: + io.rancher.os.remove: true + io.rancher.os.scope: system + links: + - cloud-init + - dockerwait # because console runs `loud-init -execute`, which may need docker + net: host + uts: host + pid: host + ipc: host + privileged: true + restart: always + volumes_from: + - all-volumes + docker: + image: rancher/os-docker:v0.4.0-dev + labels: + io.rancher.os.scope: system + links: + - cloud-init + - network + net: host + uts: host + pid: host + ipc: host + privileged: true + restart: always + volumes_from: + - all-volumes + docker-volumes: + image: rancher/os-state:v0.4.0-dev + labels: + io.rancher.os.createonly: true + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + read_only: true + volumes: + - /var/lib/rancher/conf:/var/lib/rancher/conf + - /var/lib/docker:/var/lib/docker + - /var/lib/system-docker:/var/lib/system-docker + dockerwait: + image: rancher/os-dockerwait:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + links: + - docker + net: host + uts: host + volumes_from: + - all-volumes + network: + image: rancher/os-network:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + links: + - cloud-init-pre + net: host + uts: host + privileged: true + volumes_from: + - command-volumes + - system-volumes + ntp: + image: rancher/os-ntp:v0.4.0-dev + labels: + io.rancher.os.scope: system + links: + - cloud-init + - network + net: host + uts: host + privileged: true + restart: always + preload-system-images: + image: rancher/os-preload:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + privileged: true + volumes: + - /var/run/system-docker.sock:/var/run/docker.sock + - /var/lib/system-docker/preload:/mnt/preload + volumes_from: + - command-volumes + - system-volumes + preload-user-images: + image: rancher/os-preload:v0.4.0-dev + labels: + io.rancher.os.detach: false + io.rancher.os.scope: system + links: + - dockerwait + privileged: true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker/preload:/mnt/preload + volumes_from: + - command-volumes + - system-volumes + syslog: + image: rancher/os-syslog:v0.4.0-dev + labels: + io.rancher.os.scope: system + log_driver: json-file + net: host + uts: host + privileged: true + restart: always + volumes_from: + - system-volumes + system-volumes: + image: rancher/os-state:v0.4.0-dev + labels: + io.rancher.os.createonly: true + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + read_only: true + volumes: + - /dev:/host/dev + - /os-config.yml:/os-config.yml + - /var/lib/rancher:/var/lib/rancher + - /var/lib/rancher/conf:/var/lib/rancher/conf + - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt.rancher + - /lib/modules:/lib/modules + - /lib/firmware:/lib/firmware + - /var/run:/var/run + - /var/log:/var/log + udev: + image: rancher/os-udev:v0.4.0-dev + environment: + - DAEMON=true + labels: + io.rancher.os.detach: true + io.rancher.os.scope: system + net: host + uts: host + privileged: true + restart: always + volumes_from: + - system-volumes + user-volumes: + image: rancher/os-state:v0.4.0-dev + labels: + io.rancher.os.createonly: true + io.rancher.os.scope: system + log_driver: json-file + net: none + privileged: true + read_only: true + volumes: + - /home:/home + - /opt:/opt + system_docker: + args: [docker, -d, --log-driver, syslog, -s, overlay, -b, docker-sys, --fixed-cidr, + 172.18.42.1/16, --restart=false, -g, /var/lib/system-docker, -G, root, + -H, 'unix:///var/run/system-docker.sock', --userland-proxy=false] + upgrade: + url: https://releases.rancher.com/os/versions.yml + image: rancher/os + user_docker: + tls_args: [--tlsverify, --tlscacert=ca.pem, --tlscert=server-cert.pem, --tlskey=server-key.pem, + '-H=0.0.0.0:2376'] + args: [docker, -d, -s, overlay, -G, docker, -H, 'unix:///var/run/docker.sock', --userland-proxy=false] diff --git a/util/util.go b/util/util.go index edd58786..c9e5be03 100644 --- a/util/util.go +++ b/util/util.go @@ -18,6 +18,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/mount" + "reflect" ) var ( @@ -154,6 +155,7 @@ func RandSeq(n int) string { func Convert(from, to interface{}) error { bytes, err := yaml.Marshal(from) if err != nil { + log.WithFields(log.Fields{"from": from}).Panicln(err) return err } @@ -164,37 +166,93 @@ func MergeBytes(left, right []byte) ([]byte, error) { leftMap := make(map[interface{}]interface{}) rightMap := make(map[interface{}]interface{}) - err := yaml.Unmarshal(left, &leftMap) - if err != nil { + if err := yaml.Unmarshal(left, &leftMap); err != nil { return nil, err } - err = yaml.Unmarshal(right, &rightMap) - if err != nil { + if err := yaml.Unmarshal(right, &rightMap); err != nil { return nil, err } - MergeMaps(leftMap, rightMap) - - return yaml.Marshal(leftMap) + return yaml.Marshal(MapsUnion(leftMap, rightMap, Replace)) } -func MergeMaps(left, right map[interface{}]interface{}) { - for k, v := range right { - merged := false - if existing, ok := left[k]; ok { - if rightMap, ok := v.(map[interface{}]interface{}); ok { - if leftMap, ok := existing.(map[interface{}]interface{}); ok { - merged = true - MergeMaps(leftMap, rightMap) +func Copy(d interface{}) interface{} { + switch d := d.(type) { + case map[interface{}]interface{}: + return MapCopy(d) + default: + return d + } +} + +func Replace(l, r interface{}) interface{} { + return r +} + +func Equal(l, r interface{}) interface{} { + if reflect.DeepEqual(l, r) { + return l + } + return nil +} + +func MapsUnion(left, right map[interface{}]interface{}, op func(interface{}, interface{}) interface{}) map[interface{}]interface{} { + result := MapCopy(left) + + for k, r := range right { + if l, ok := result[k]; ok { + switch l := l.(type) { + case map[interface{}]interface{}: + switch r := r.(type) { + case map[interface{}]interface{}: + result[k] = MapsUnion(l, r, op) + default: + result[k] = op(l, r) + } + default: + result[k] = op(l, r) + } + } else { + result[k] = Copy(r) + } + } + + return result +} + +func MapsIntersection(left, right map[interface{}]interface{}, op func(interface{}, interface{}) interface{}) map[interface{}]interface{} { + result := map[interface{}]interface{}{} + + for k, l := range left { + if r, ok := right[k]; ok { + switch r := r.(type) { + case map[interface{}]interface{}: + switch l := l.(type) { + case map[interface{}]interface{}: + result[k] = MapsIntersection(l, r, op) + default: + if i := op(l, r); i != nil { + result[k] = i + } + } + default: + if i := op(l, r); i != nil { + result[k] = i } } } - - if !merged { - left[k] = v - } } + + return result +} + +func MapCopy(data map[interface{}]interface{}) map[interface{}]interface{} { + result := map[interface{}]interface{}{} + for k, v := range data { + result[k] = Copy(v) + } + return result } func GetServices(urls []string) ([]string, error) {