diff --git a/cmd/control/service.go b/cmd/control/service.go index 11c63a3f..d43bba3b 100644 --- a/cmd/control/service.go +++ b/cmd/control/service.go @@ -6,7 +6,6 @@ import ( "github.com/codegangsta/cli" "github.com/rancherio/os/config" - "github.com/rancherio/os/util" ) func serviceSubCommands() []cli.Command { @@ -37,21 +36,16 @@ func disable(c *cli.Context) { } for _, service := range c.Args() { - filtered := make([]string, 0, len(c.Args())) - for _, existing := range cfg.EnabledServices { - if existing != service { - filtered = append(filtered, existing) - } + if _, ok := cfg.Services[service]; !ok { + continue } - if len(filtered) != len(c.Args()) { - cfg.EnabledServices = filtered - changed = true - } + cfg.Services[service] = false + changed = true } if changed { - if err = cfg.Set("enabled_services", cfg.EnabledServices); err != nil { + if err = cfg.Set("services", cfg.Services); err != nil { log.Fatal(err) } } @@ -65,14 +59,14 @@ func enable(c *cli.Context) { } for _, service := range c.Args() { - if !util.Contains(cfg.EnabledServices, service) { - cfg.EnabledServices = append(cfg.EnabledServices, service) + if val, ok := cfg.Services[service]; !ok || !val { + cfg.Services[service] = true changed = true } } if changed { - if err = cfg.Set("enabled_services", cfg.EnabledServices); err != nil { + if err = cfg.Set("services", cfg.Services); err != nil { log.Fatal(err) } } @@ -84,22 +78,11 @@ func list(c *cli.Context) { log.Fatal(err) } - enabled := map[string]bool{} - - for _, service := range cfg.EnabledServices { - enabled[service] = true - } - - for service, _ := range cfg.Services { - if _, ok := enabled[service]; ok { - delete(enabled, service) + for service, enabled := range cfg.Services { + if enabled { fmt.Printf("enabled %s\n", service) } else { fmt.Printf("disabled %s\n", service) } } - - for service, _ := range enabled { - fmt.Printf("enabled %s\n", service) - } } diff --git a/config/default.go b/config/default.go index 403f9f45..4beb32a1 100644 --- a/config/default.go +++ b/config/default.go @@ -301,8 +301,10 @@ func NewConfig() *Config { Net: "host", }, }, - EnabledServices: []string{}, - Services: map[string]Config{ + Services: map[string]bool{ + "ubuntu-console": false, + }, + BundledServices: map[string]Config{ "ubuntu-console": { SystemContainers: map[string]*project.ServiceConfig{ "console": { diff --git a/config/types.go b/config/types.go index b775945a..0aee7d06 100644 --- a/config/types.go +++ b/config/types.go @@ -43,14 +43,15 @@ type ContainerConfig struct { } type Config struct { - Services map[string]Config `yaml:"services,omitempty"` + Environment map[string]string `yaml:"environment,omitempty"` + BundledServices map[string]Config `yaml:"bundled_services,omitempty"` BootstrapContainers map[string]*project.ServiceConfig `yaml:"bootstrap_containers,omitempty"` BootstrapDocker DockerConfig `yaml:"bootstrap_docker,omitempty"` CloudInit CloudInit `yaml:"cloud_init,omitempty"` Console ConsoleConfig `yaml:"console,omitempty"` Debug bool `yaml:"debug,omitempty"` Disable []string `yaml:"disable,omitempty"` - EnabledServices []string `yaml:"enabled_services,omitempty"` + Services map[string]bool `yaml:"services,omitempty"` Modules []string `yaml:"modules,omitempty"` Network NetworkConfig `yaml:"network,omitempty"` Ssh SshConfig `yaml:"ssh,omitempty"` diff --git a/docker/container.go b/docker/container.go index f4a5d427..f5690f8c 100644 --- a/docker/container.go +++ b/docker/container.go @@ -170,11 +170,36 @@ func (c *Container) Reset() *Container { return c } +func (c *Container) requiresSyslog() bool { + return (c.ContainerCfg.Service.LogDriver == "" || c.ContainerCfg.Service.LogDriver == "syslog") +} + +func (c *Container) hasLink(link string) bool { + return util.Contains(c.ContainerCfg.Service.Links, link) +} + +func (c *Container) addLink(link string) { + c.ContainerCfg.Service.Links = append(c.ContainerCfg.Service.Links, link) +} + func (c *Container) parseService() { - if (c.ContainerCfg.Service.LogDriver == "" || c.ContainerCfg.Service.LogDriver == "syslog") && - !util.Contains(c.ContainerCfg.Service.Links, "syslog") { + client, err := NewClient(c.dockerHost) + if err != nil { + c.Err = err + return + } + + if c.ContainerCfg.Service.Image != "" { + i, _ := client.InspectImage(c.ContainerCfg.Service.Image) + if i == nil && !c.hasLink("network") { + log.Debugf("Adding network link to %s", c.Name) + c.addLink("network") + } + } + + if c.requiresSyslog() && !c.hasLink("syslog") { log.Debugf("Adding syslog link to %s\n", c.Name) - c.ContainerCfg.Service.Links = append(c.ContainerCfg.Service.Links, "syslog") + c.addLink("syslog") } cfg, hostConfig, err := docker.Convert(c.ContainerCfg.Service) diff --git a/init/sysinit.go b/init/sysinit.go index 723b88d1..cca1b8a8 100644 --- a/init/sysinit.go +++ b/init/sysinit.go @@ -110,40 +110,51 @@ func loadImages(cfg *config.Config) error { } func runServices(name string, cfg *config.Config, configs map[string]*project.ServiceConfig) error { - project := project.NewProject(name, docker.NewContainerFactory(cfg)) + network := false + projectEvents := make(chan project.ProjectEvent) + p := project.NewProject(name, docker.NewContainerFactory(cfg)) + p.AddListener(projectEvents) enabled := make(map[string]bool) for name, serviceConfig := range configs { - if err := project.AddConfig(name, serviceConfig); err != nil { + if err := p.AddConfig(name, serviceConfig); err != nil { log.Infof("Failed loading service %s", name) } } - project.ReloadCallback = func() error { + p.ReloadCallback = func() error { err := cfg.Reload() if err != nil { return err } - for _, service := range cfg.EnabledServices { + for service, serviceEnabled := range cfg.Services { + if !serviceEnabled { + continue + } + if _, ok := enabled[service]; ok { continue } - if config, ok := cfg.Services[service]; ok { + if config, ok := cfg.BundledServices[service]; ok { for name, s := range config.SystemContainers { - if err := project.AddConfig(name, s); err != nil { + if err := p.AddConfig(name, s); err != nil { log.Errorf("Failed to load %s : %v", name, err) } } } else { - bytes, err := util.LoadResource(service) + bytes, err := util.LoadResource(service, network) if err != nil { - log.Errorf("Failed to load %s : %v", service, err) + if err == util.ErrNoNetwork { + log.Debugf("Can not load %s, networking not enabled", service) + } else { + log.Errorf("Failed to load %s : %v", service, err) + } continue } - err = project.Load(bytes) + err = p.Load(bytes) if err != nil { log.Errorf("Failed to load %s : %v", service, err) continue @@ -156,12 +167,20 @@ func runServices(name string, cfg *config.Config, configs map[string]*project.Se return nil } - err := project.ReloadCallback() + go func() { + for event := range projectEvents { + if event.Event == project.CONTAINER_STARTED && event.Service.Name() == "network" { + network = true + } + } + }() + + err := p.ReloadCallback() if err != nil { log.Errorf("Failed to reload %s : %v", name, err) return err } - return project.Up() + return p.Up() } func runContainers(cfg *config.Config) error { diff --git a/util/util.go b/util/util.go index b97b9aaa..162612e6 100644 --- a/util/util.go +++ b/util/util.go @@ -2,6 +2,7 @@ package util import ( "archive/tar" + "errors" "fmt" "io" "io/ioutil" @@ -17,7 +18,8 @@ import ( ) var ( - letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + ErrNoNetwork = errors.New("Networking not available to load resource") ) func mountProc() error { @@ -168,6 +170,25 @@ func Convert(from, to interface{}) error { return yaml.Unmarshal(bytes, to) } +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 { + return nil, err + } + + err = yaml.Unmarshal(right, &rightMap) + if err != nil { + return nil, err + } + + MergeMaps(leftMap, rightMap) + + return yaml.Marshal(leftMap) +} + func MergeMaps(left, right map[interface{}]interface{}) { for k, v := range right { merged := false @@ -186,8 +207,11 @@ func MergeMaps(left, right map[interface{}]interface{}) { } } -func LoadResource(location string) ([]byte, error) { +func LoadResource(location string, network bool) ([]byte, error) { if strings.HasPrefix(location, "http:/") || strings.HasPrefix(location, "https:/") { + if !network { + return nil, ErrNoNetwork + } resp, err := http.Get(location) if err != nil { return nil, err