1
0
mirror of https://github.com/rancher/os.git synced 2025-06-30 08:41:48 +00:00

Refactor configuration to compose syntax

This commit is contained in:
Darren Shepherd 2015-03-29 02:57:15 -07:00
parent 38389b1f8e
commit c7ae14cc13
11 changed files with 535 additions and 344 deletions

View File

@ -8,26 +8,26 @@ import (
"github.com/rancherio/os/docker" "github.com/rancherio/os/docker"
) )
func parseContainers(cfg *config.Config) map[string]*docker.Container { //func parseContainers(cfg *config.Config) map[string]*docker.Container {
result := map[string]*docker.Container{} // result := map[string]*docker.Container{}
//
for _, containerConfig := range cfg.SystemContainers { // for _, containerConfig := range cfg.SystemContainers {
container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &containerConfig) // container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &containerConfig)
if containerConfig.Id != "" { // if containerConfig.Id != "" {
result[containerConfig.Id] = container // result[containerConfig.Id] = container
} // }
} // }
//
return result // return result
} //}
func reload(c *cli.Context) { func reload(c *cli.Context) {
cfg, err := config.LoadConfig() _, err := config.LoadConfig()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
containers := parseContainers(cfg) containers := map[string]*docker.Container{} //parseContainers(cfg)
toStart := make([]*docker.Container, 0, len(c.Args())) toStart := make([]*docker.Container, 0, len(c.Args()))
for _, id := range c.Args() { for _, id := range c.Args() {

View File

@ -27,10 +27,10 @@ func Main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
applyNetworkConfigs(cfg) ApplyNetworkConfigs(&cfg.Network)
} }
func applyNetworkConfigs(cfg *config.Config) error { func ApplyNetworkConfigs(netCfg *config.NetworkConfig) error {
links, err := netlink.LinkList() links, err := netlink.LinkList()
if err != nil { if err != nil {
return err return err
@ -41,7 +41,7 @@ func applyNetworkConfigs(cfg *config.Config) error {
linkName := link.Attrs().Name linkName := link.Attrs().Name
var match config.InterfaceConfig var match config.InterfaceConfig
for key, netConf := range cfg.Network.Interfaces { for key, netConf := range netCfg.Interfaces {
if netConf.Match == "" { if netConf.Match == "" {
netConf.Match = key netConf.Match = key
} }
@ -86,8 +86,8 @@ func applyNetworkConfigs(cfg *config.Config) error {
} }
//post run //post run
if cfg.Network.PostRun != nil { if netCfg.PostRun != nil {
return docker.StartAndWait(config.DOCKER_SYSTEM_HOST, cfg.Network.PostRun) return docker.StartAndWait(config.DOCKER_SYSTEM_HOST, netCfg.PostRun)
} }
return nil return nil
} }

View File

@ -5,6 +5,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/rancherio/rancher-compose/project"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/rancherio/os/util" "github.com/rancherio/os/util"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -16,23 +18,10 @@ func (c *Config) privilegedMerge(newConfig Config) error {
return err return err
} }
toAppend := make([]ContainerConfig, 0, 5) for k, v := range newConfig.SystemContainers {
c.SystemContainers[k] = v
for _, newContainer := range newConfig.SystemContainers {
found := false
for i, existingContainer := range c.SystemContainers {
if existingContainer.Id != "" && newContainer.Id == existingContainer.Id {
found = true
c.SystemContainers[i] = newContainer
}
}
if !found {
toAppend = append(toAppend, newContainer)
}
} }
c.SystemContainers = append(c.SystemContainers, toAppend...)
return nil return nil
} }
@ -42,8 +31,8 @@ func (c *Config) overlay(newConfig Config) error {
} }
func (c *Config) clearReadOnly() { func (c *Config) clearReadOnly() {
c.BootstrapContainers = make([]ContainerConfig, 0) c.BootstrapContainers = make(map[string]*project.ServiceConfig, 0)
c.SystemContainers = make([]ContainerConfig, 0) c.SystemContainers = make(map[string]*project.ServiceConfig, 0)
} }
func clearReadOnly(data map[interface{}]interface{}) map[interface{}]interface{} { func clearReadOnly(data map[interface{}]interface{}) map[interface{}]interface{} {
@ -103,6 +92,12 @@ func LoadConfig() (*Config, error) {
if cfg.Debug { if cfg.Debug {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
if !util.Contains(cfg.UserDocker.Args, "-D") {
cfg.UserDocker.Args = append(cfg.UserDocker.Args, "-D")
}
if !util.Contains(cfg.SystemDocker.Args, "-D") {
cfg.SystemDocker.Args = append(cfg.SystemDocker.Args, "-D")
}
} }
return cfg, nil return cfg, nil
@ -197,20 +192,11 @@ func Dump(private, full bool) (string, error) {
} }
func (c *Config) configureConsole() error { func (c *Config) configureConsole() error {
if !c.Console.Persistent { if console, ok := c.SystemContainers[CONSOLE_CONTAINER]; ok {
return nil if c.Console.Persistent {
} console.Labels = append(console.Labels, REMOVE+"=false")
} else {
for i := range c.SystemContainers { console.Labels = append(console.Labels, REMOVE+"=true")
// Need to modify original object, not the copy
var container *ContainerConfig = &c.SystemContainers[i]
if container.Id != CONSOLE_CONTAINER {
continue
}
if strings.Contains(container.Cmd, "--rm ") {
container.Cmd = strings.Replace(container.Cmd, "--rm ", "", 1)
} }
} }
@ -221,7 +207,6 @@ func (c *Config) readGlobals() error {
return util.ShortCircuit( return util.ShortCircuit(
c.readCmdline, c.readCmdline,
c.readArgs, c.readArgs,
c.mergeAddons,
c.configureConsole, c.configureConsole,
) )
} }
@ -233,19 +218,6 @@ func (c *Config) Reload() error {
) )
} }
func (c *Config) mergeAddons() error {
for _, addon := range c.EnabledAddons {
if newConfig, ok := c.Addons[addon]; ok {
log.Debugf("Enabling addon %s", addon)
if err := c.privilegedMerge(newConfig); err != nil {
return err
}
}
}
return nil
}
func (c *Config) Get(key string) (interface{}, error) { func (c *Config) Get(key string) (interface{}, error) {
data := make(map[interface{}]interface{}) data := make(map[interface{}]interface{})
err := util.Convert(c, &data) err := util.Convert(c, &data)

View File

@ -1,5 +1,9 @@
package config package config
import (
"github.com/rancherio/rancher-compose/project"
)
func NewConfig() *Config { func NewConfig() *Config {
return &Config{ return &Config{
Debug: DEBUG, Debug: DEBUG,
@ -43,7 +47,7 @@ func NewConfig() *Config {
Nameservers: []string{"8.8.8.8", "8.8.4.4"}, Nameservers: []string{"8.8.8.8", "8.8.4.4"},
}, },
Interfaces: map[string]InterfaceConfig{ Interfaces: map[string]InterfaceConfig{
"eth*": { "eth0": {
DHCP: true, DHCP: true,
}, },
"lo": { "lo": {
@ -58,193 +62,238 @@ func NewConfig() *Config {
Url: "https://releases.rancher.com/os/versions.yml", Url: "https://releases.rancher.com/os/versions.yml",
Image: "rancher/os", Image: "rancher/os",
}, },
BootstrapContainers: []ContainerConfig{ BootstrapContainers: map[string]*project.ServiceConfig{
{ "udev": {
Id: "udev", Net: "host",
Cmd: "--name=udev " + Privileged: true,
"--net=none " + Labels: []string{
"--privileged " + DETACH + "=false",
"--rm " + },
"-v=/dev:/host/dev " + Volumes: []string{
"-v=/lib/modules:/lib/modules:ro " + "/dev:/host/dev",
"udev", "/lib/modules:/lib/modules:ro",
"/lib/firmware:/lib/firmware:ro",
},
Image: "udev",
}, },
}, },
SystemContainers: []ContainerConfig{ SystemContainers: map[string]*project.ServiceConfig{
{ "udev": {
Id: "udev", Image: "udev",
Cmd: "--name=udev " + Net: "host",
"--net=none " + Privileged: true,
"--privileged " + Labels: []string{
"--rm " + DETACH + "=true",
"-v=/dev:/host/dev " + },
"-v=/lib/modules:/lib/modules:ro " + Environment: []string{
"udev", "DAEMON=true",
CreateOnly: true, },
Volumes: []string{
"/dev:/host/dev",
"/lib/modules:/lib/modules:ro",
"/lib/firmware:/lib/firmware:ro",
},
}, },
{ "system-volumes": {
Id: "system-volumes", Image: "state",
Cmd: "--name=system-volumes " + Net: "none",
"--net=none " + ReadOnly: true,
"--read-only " + Privileged: true,
"-v=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt " + Labels: []string{
"-v=/var/lib/rancher/conf:/var/lib/rancher/conf " + CREATE_ONLY + "=true",
"-v=/lib/modules:/lib/modules:ro " + },
"-v=/var/run:/var/run " + Volumes: []string{
"-v=/var/log:/var/log " + "/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt",
"state", "/var/lib/rancher/conf:/var/lib/rancher/conf",
CreateOnly: true, "/lib/modules:/lib/modules:ro",
"/lib/firmware:/lib/firmware:ro",
"/var/run:/var/run",
"/var/log:/var/log",
},
}, },
{ "command-volumes": {
Id: "command-volumes", Image: "state",
Cmd: "--name=command-volumes " + Net: "none",
"--net=none " + ReadOnly: true,
"--read-only " + Privileged: true,
"-v=/init:/sbin/halt:ro " + Labels: []string{
"-v=/init:/sbin/poweroff:ro " + CREATE_ONLY + "=true",
"-v=/init:/sbin/reboot:ro " + },
"-v=/init:/sbin/shutdown:ro " + Volumes: []string{
"-v=/init:/sbin/netconf:ro " + "/init:/sbin/halt:ro",
"-v=/init:/usr/bin/cloud-init:ro " + "/init:/sbin/poweroff:ro",
"-v=/init:/usr/bin/rancherctl:ro " + "/init:/sbin/reboot:ro",
"-v=/init:/usr/bin/respawn:ro " + "/init:/sbin/shutdown:ro",
"-v=/init:/usr/bin/system-docker:ro " + "/init:/sbin/netconf:ro",
"-v=/lib/modules:/lib/modules:ro " + "/init:/usr/bin/cloud-init:ro",
"-v=/usr/bin/docker:/usr/bin/docker:ro " + "/init:/usr/bin/rancherctl:ro",
"state", "/init:/usr/bin/respawn:ro",
CreateOnly: true, "/init:/usr/bin/system-docker:ro",
"/lib/modules:/lib/modules:ro",
"/usr/bin/docker:/usr/bin/docker:ro",
},
}, },
{ "user-volumes": {
Id: "user-volumes", Image: "state",
Cmd: "--name=user-volumes " + Net: "none",
"--net=none " + ReadOnly: true,
"--read-only " + Privileged: true,
"-v=/home:/home " + Labels: []string{
"-v=/opt:/opt " + CREATE_ONLY + "=true",
"state", },
CreateOnly: true, Volumes: []string{
"/home:/home",
"/opt:/opt",
},
}, },
{ "docker-volumes": {
Id: "docker-volumes", Image: "state",
Cmd: "--name=docker-volumes " + Net: "none",
"--net=none " + ReadOnly: true,
"--read-only " + Privileged: true,
"-v=/var/lib/rancher:/var/lib/rancher " + Labels: []string{
"-v=/var/lib/docker:/var/lib/docker " + CREATE_ONLY + "=true",
"-v=/var/lib/system-docker:/var/lib/system-docker " + },
"state", Volumes: []string{
CreateOnly: true, "/var/lib/rancher:/var/lib/rancher",
"/var/lib/docker:/var/lib/docker",
"/var/lib/system-docker:/var/lib/system-docker",
},
}, },
{ "all-volumes": {
Id: "all-volumes", Image: "state",
Cmd: "--name=all-volumes " + Net: "none",
"--rm " + ReadOnly: true,
"--net=none " + Privileged: true,
"--read-only " + Labels: []string{
"--volumes-from=docker-volumes " + CREATE_ONLY + "=true",
"--volumes-from=command-volumes " + },
"--volumes-from=user-volumes " + VolumesFrom: []string{
"--volumes-from=system-volumes " + "docker-volumes",
"state", "command-volumes",
CreateOnly: true, "user-volumes",
"system-volumes",
},
}, },
{ "cloud-init-pre": {
Id: "cloud-init-pre", Image: "cloudinit",
Cmd: "--name=cloud-init-pre " + Privileged: true,
"--rm " + Net: "host",
"--privileged " + Labels: []string{
"--net=host " + RELOAD_CONFIG + "=true",
"-e CLOUD_INIT_NETWORK=false " + DETACH + "=false",
"--volumes-from=command-volumes " + },
"--volumes-from=system-volumes " + Environment: []string{
"cloudinit", "CLOUD_INIT_NETWORK=false",
ReloadConfig: true, },
VolumesFrom: []string{
"command-volumes",
"system-volumes",
},
}, },
{ "network": {
Id: "network", Image: "network",
Cmd: "--name=network " + CapAdd: []string{
"--rm " + "NET_ADMIN",
"--cap-add=NET_ADMIN " + },
"--net=host " + Net: "host",
"--volumes-from=command-volumes " + Labels: []string{
"--volumes-from=system-volumes " + DETACH + "=false",
},
Links: []string{
"cloud-init-pre",
},
VolumesFrom: []string{
"command-volumes",
"system-volumes",
},
},
"cloud-init": {
Image: "cloudinit",
Privileged: true,
Labels: []string{
RELOAD_CONFIG + "=true",
DETACH + "=false",
},
Net: "host",
Links: []string{
"cloud-init-pre",
"network", "network",
},
VolumesFrom: []string{
"command-volumes",
"system-volumes",
},
}, },
{ "ntp": {
Id: "cloud-init", Image: "ntp",
Cmd: "--name=cloud-init " + Privileged: true,
"--rm " + Net: "host",
"--privileged " + Links: []string{
"--net=host " + "cloud-init",
"--volumes-from=command-volumes " + "network",
"--volumes-from=system-volumes " + },
"cloudinit",
ReloadConfig: true,
}, },
{ "syslog": {
Id: "ntp", Image: "syslog",
Cmd: "--name=ntp " + Privileged: true,
"--rm " + Net: "host",
"-d " + Links: []string{
"--privileged " + "cloud-init",
"--net=host " + "network",
"ntp", },
VolumesFrom: []string{
"system-volumes",
},
}, },
{ "userdocker": {
Id: "syslog", Image: "userdocker",
Cmd: "--name=syslog " + Privileged: true,
"-d " + Pid: "host",
"--rm " + Ipc: "host",
"--privileged " + Net: "host",
"--net=host " + Links: []string{
"--ipc=host " + "network",
"--pid=host " + },
"--volumes-from=system-volumes " + VolumesFrom: []string{
"syslog", "all-volumes",
},
}, },
{ "console": {
Id: "userdocker", Image: "console",
Cmd: "--name=userdocker " + Privileged: true,
"-d " + Links: []string{
"--rm " + "cloud-init",
"--restart=always " + },
"--ipc=host " + VolumesFrom: []string{
"--pid=host " + "all-volumes",
"--net=host " + },
"--privileged " + Restart: "always",
"--volumes-from=all-volumes " + Pid: "host",
"userdocker", Ipc: "host",
}, Net: "host",
{
Id: "console",
Cmd: "--name=console " +
"-d " +
"--rm " +
"--privileged " +
"--volumes-from=all-volumes " +
"--restart=always " +
"--ipc=host " +
"--net=host " +
"--pid=host " +
"console",
}, },
}, },
EnabledAddons: []string{}, EnabledAddons: []string{},
Addons: map[string]Config{ Addons: map[string]Config{
"ubuntu-console": { "ubuntu-console": {
SystemContainers: []ContainerConfig{ SystemContainers: map[string]*project.ServiceConfig{
{ "console": {
Id: "console", Image: "rancher/ubuntuconsole:" + VERSION,
Cmd: "--name=ubuntu-console " + Privileged: true,
"-d " + Labels: []string{
"--rm " + DETACH + "=true",
"--privileged " + },
"--volumes-from=all-volumes " + Links: []string{
"--restart=always " + "cloud-init",
"--ipc=host " + },
"--net=host " + VolumesFrom: []string{
"--pid=host " + "all-volumes",
"rancher/ubuntuconsole:" + VERSION, },
Restart: "always",
Pid: "host",
Ipc: "host",
Net: "host",
}, },
}, },
}, },

View File

@ -1,5 +1,7 @@
package config package config
import "github.com/rancherio/rancher-compose/project"
const ( const (
CONSOLE_CONTAINER = "console" CONSOLE_CONTAINER = "console"
DOCKER_BIN = "/usr/bin/docker" DOCKER_BIN = "/usr/bin/docker"
@ -12,6 +14,14 @@ const (
USER_INIT = "/sbin/init-user" USER_INIT = "/sbin/init-user"
MODULES_ARCHIVE = "/modules.tar" MODULES_ARCHIVE = "/modules.tar"
DEBUG = false DEBUG = false
LABEL = "label"
HASH = "io.rancher.os.hash"
ID = "io.rancher.os.id"
DETACH = "io.rancher.os.detach"
REMOVE = "io.rancher.os.remove"
CREATE_ONLY = "io.rancher.os.createonly"
RELOAD_CONFIG = "io.rancher.os.reloadconfig"
) )
var ( var (
@ -22,30 +32,31 @@ var (
) )
type ContainerConfig struct { type ContainerConfig struct {
Id string `yaml:"id,omitempty"` Id string `yaml:"id,omitempty"`
Cmd string `yaml:"run,omitempty"` Cmd string `yaml:"run,omitempty"`
MigrateVolumes bool `yaml:"migrate_volumes,omitempty"` MigrateVolumes bool `yaml:"migrate_volumes,omitempty"`
ReloadConfig bool `yaml:"reload_config,omitempty"` ReloadConfig bool `yaml:"reload_config,omitempty"`
CreateOnly bool `yaml:create_only,omitempty` CreateOnly bool `yaml:create_only,omitempty`
Service *project.ServiceConfig `yaml:service,omitempty`
} }
type Config struct { type Config struct {
Addons map[string]Config `yaml:"addons,omitempty"` Addons map[string]Config `yaml:"addons,omitempty"`
BootstrapContainers []ContainerConfig `yaml:"bootstrap_containers,omitempty"` BootstrapContainers map[string]*project.ServiceConfig `yaml:"bootstrap_containers,omitempty"`
CloudInit CloudInit `yaml:"cloud_init,omitempty"` CloudInit CloudInit `yaml:"cloud_init,omitempty"`
Console ConsoleConfig `yaml:"console,omitempty"` Console ConsoleConfig `yaml:"console,omitempty"`
Debug bool `yaml:"debug,omitempty"` Debug bool `yaml:"debug,omitempty"`
Disable []string `yaml:"disable,omitempty"` //Disable []string `yaml:"disable,omitempty"`
EnabledAddons []string `yaml:"enabled_addons,omitempty"` EnabledAddons []string `yaml:"enabled_addons,omitempty"`
Modules []string `yaml:"modules,omitempty"` Modules []string `yaml:"modules,omitempty"`
Network NetworkConfig `yaml:"network,omitempty"` Network NetworkConfig `yaml:"network,omitempty"`
Ssh SshConfig `yaml:"ssh,omitempty"` Ssh SshConfig `yaml:"ssh,omitempty"`
State StateConfig `yaml:"state,omitempty"` State StateConfig `yaml:"state,omitempty"`
SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemContainers map[string]*project.ServiceConfig `yaml:"system_containers,omitempty"`
SystemDocker DockerConfig `yaml:"system_docker,omitempty"` SystemDocker DockerConfig `yaml:"system_docker,omitempty"`
Upgrade UpgradeConfig `yaml:"upgrade,omitempty"` Upgrade UpgradeConfig `yaml:"upgrade,omitempty"`
UserContainers []ContainerConfig `yaml:"user_containers,omitempty"` UserContainers []ContainerConfig `yaml:"user_containers,omitempty"`
UserDocker DockerConfig `yaml:"user_docker,omitempty"` UserDocker DockerConfig `yaml:"user_docker,omitempty"`
} }
type ConsoleConfig struct { type ConsoleConfig struct {

View File

@ -3,6 +3,8 @@ package docker
import ( import (
"time" "time"
log "github.com/Sirupsen/logrus"
dockerClient "github.com/fsouza/go-dockerclient" dockerClient "github.com/fsouza/go-dockerclient"
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
) )
@ -26,12 +28,16 @@ func NewClient(endpoint string) (*dockerClient.Client, error) {
return nil, err return nil, err
} }
retry := false
for i := 0; i < (MAX_WAIT / INTERVAL); i++ { for i := 0; i < (MAX_WAIT / INTERVAL); i++ {
_, err = client.Info() _, err = client.Info()
if err == nil { if err == nil {
break break
} }
retry = true
log.Infof("Waiting for Docker at %s", endpoint)
time.Sleep(INTERVAL * time.Millisecond) time.Sleep(INTERVAL * time.Millisecond)
} }
@ -39,5 +45,8 @@ func NewClient(endpoint string) (*dockerClient.Client, error) {
return nil, err return nil, err
} }
if retry {
log.Infof("Connected to Docker at %s", endpoint)
}
return client, nil return client, nil
} }

View File

@ -4,6 +4,7 @@ import (
"crypto/sha1" "crypto/sha1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
"sort" "sort"
@ -16,12 +17,8 @@ import (
dockerClient "github.com/fsouza/go-dockerclient" dockerClient "github.com/fsouza/go-dockerclient"
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
"github.com/rancherio/os/util" "github.com/rancherio/os/util"
) "github.com/rancherio/rancher-compose/docker"
"github.com/rancherio/rancher-compose/project"
const (
LABEL = "label"
HASH = "io.rancher.os.hash"
ID = "io.rancher.os.id"
) )
type Container struct { type Container struct {
@ -48,6 +45,10 @@ func getHash(containerCfg *config.ContainerConfig) (string, error) {
w.Write([]byte(containerCfg.Id)) w.Write([]byte(containerCfg.Id))
w.Write([]byte(containerCfg.Cmd)) w.Write([]byte(containerCfg.Cmd))
if containerCfg.Service != nil {
//TODO: properly hash
w.Write([]byte(fmt.Sprintf("%v", containerCfg.Service)))
}
if w.Err != nil { if w.Err != nil {
return "", w.Err return "", w.Err
@ -61,6 +62,18 @@ func StartAndWait(dockerHost string, containerCfg *config.ContainerConfig) error
return container.Err return container.Err
} }
func NewContainerFromService(dockerHost string, name string, service *project.ServiceConfig) *Container {
c := &Container{
Name: name,
dockerHost: dockerHost,
ContainerCfg: &config.ContainerConfig{
Id: name,
Service: service,
},
}
return c.Parse()
}
func NewContainer(dockerHost string, containerCfg *config.ContainerConfig) *Container { func NewContainer(dockerHost string, containerCfg *config.ContainerConfig) *Container {
c := &Container{ c := &Container{
dockerHost: dockerHost, dockerHost: dockerHost,
@ -78,7 +91,7 @@ func getByLabel(client *dockerClient.Client, key, value string) (*dockerClient.A
containers, err := client.ListContainers(dockerClient.ListContainersOptions{ containers, err := client.ListContainers(dockerClient.ListContainersOptions{
All: true, All: true,
Filters: map[string][]string{ Filters: map[string][]string{
LABEL: []string{fmt.Sprintf("%s=%s", key, value)}, config.LABEL: []string{fmt.Sprintf("%s=%s", key, value)},
}, },
}) })
@ -114,7 +127,7 @@ func (c *Container) Lookup() *Container {
containers, err := client.ListContainers(dockerClient.ListContainersOptions{ containers, err := client.ListContainers(dockerClient.ListContainersOptions{
All: true, All: true,
Filters: map[string][]string{ Filters: map[string][]string{
LABEL: []string{fmt.Sprintf("%s=%s", HASH, hash)}, config.LABEL: []string{fmt.Sprintf("%s=%s", config.HASH, hash)},
}, },
}) })
if err != nil { if err != nil {
@ -157,11 +170,23 @@ func (c *Container) Reset() *Container {
return c return c
} }
func (c *Container) Parse() *Container { func (c *Container) parseService() {
if c.Config != nil || c.Err != nil { cfg, hostConfig, err := docker.Convert(c.ContainerCfg.Service)
return c if err != nil {
c.Err = err
return
} }
c.Config = cfg
c.HostConfig = hostConfig
c.detach = c.Config.Labels[config.DETACH] != "false"
c.remove = c.Config.Labels[config.REMOVE] != "false"
c.ContainerCfg.CreateOnly = c.Config.Labels[config.CREATE_ONLY] == "true"
c.ContainerCfg.ReloadConfig = c.Config.Labels[config.RELOAD_CONFIG] == "true"
}
func (c *Container) parseCmd() {
flags := flag.NewFlagSet("run", flag.ExitOnError) flags := flag.NewFlagSet("run", flag.ExitOnError)
flRemove := flags.Bool([]string{"#rm", "-rm"}, false, "") flRemove := flags.Bool([]string{"#rm", "-rm"}, false, "")
@ -170,7 +195,8 @@ func (c *Container) Parse() *Container {
args, err := shlex.Split(c.ContainerCfg.Cmd) args, err := shlex.Split(c.ContainerCfg.Cmd)
if err != nil { if err != nil {
return c.returnErr(err) c.Err = err
return
} }
log.Debugf("Parsing [%s]", strings.Join(args, ",")) log.Debugf("Parsing [%s]", strings.Join(args, ","))
@ -179,6 +205,21 @@ func (c *Container) Parse() *Container {
c.Name = *flName c.Name = *flName
c.detach = *flDetach c.detach = *flDetach
c.remove = *flRemove c.remove = *flRemove
}
func (c *Container) Parse() *Container {
if c.Config != nil || c.Err != nil {
return c
}
if len(c.ContainerCfg.Cmd) > 0 {
c.parseCmd()
} else if c.ContainerCfg.Service != nil {
c.parseService()
} else {
c.Err = errors.New("Cmd or Service must be set")
return c
}
if c.ContainerCfg.Id == "" { if c.ContainerCfg.Id == "" {
c.ContainerCfg.Id = c.Name c.ContainerCfg.Id = c.Name
@ -219,6 +260,7 @@ func (c *Container) Stage() *Container {
OutputStream: os.Stdout, OutputStream: os.Stdout,
}, dockerClient.AuthConfiguration{}) }, dockerClient.AuthConfiguration{})
} else if err != nil { } else if err != nil {
log.Errorf("Failed to stage: %s: %v", c.Config.Image, err)
c.Err = err c.Err = err
} }
@ -291,7 +333,7 @@ func (c *Container) renameOld(client *dockerClient.Client, opts *dockerClient.Cr
} }
var newName string var newName string
if label, ok := existing.Config.Labels[HASH]; ok { if label, ok := existing.Config.Labels[config.HASH]; ok {
newName = fmt.Sprintf("%s-%s", existing.Name, label) newName = fmt.Sprintf("%s-%s", existing.Name, label)
} else { } else {
newName = fmt.Sprintf("%s-unknown-%s", existing.Name, util.RandSeq(12)) newName = fmt.Sprintf("%s-unknown-%s", existing.Name, util.RandSeq(12))
@ -316,6 +358,7 @@ func (c *Container) renameOld(client *dockerClient.Client, opts *dockerClient.Cr
func (c *Container) getCreateOpts(client *dockerClient.Client) (*dockerClient.CreateContainerOptions, error) { func (c *Container) getCreateOpts(client *dockerClient.Client) (*dockerClient.CreateContainerOptions, error) {
bytes, err := json.Marshal(c) bytes, err := json.Marshal(c)
if err != nil { if err != nil {
log.Errorf("Failed to marshall: %v", c)
return nil, err return nil, err
} }
@ -323,6 +366,7 @@ func (c *Container) getCreateOpts(client *dockerClient.Client) (*dockerClient.Cr
err = json.Unmarshal(bytes, &opts) err = json.Unmarshal(bytes, &opts)
if err != nil { if err != nil {
log.Errorf("Failed to unmarshall: %s", string(bytes))
return nil, err return nil, err
} }
@ -335,8 +379,8 @@ func (c *Container) getCreateOpts(client *dockerClient.Client) (*dockerClient.Cr
return nil, err return nil, err
} }
opts.Config.Labels[HASH] = hash opts.Config.Labels[config.HASH] = hash
opts.Config.Labels[ID] = c.ContainerCfg.Id opts.Config.Labels[config.ID] = c.ContainerCfg.Id
return &opts, nil return &opts, nil
} }
@ -346,7 +390,7 @@ func appendVolumesFrom(client *dockerClient.Client, containerCfg *config.Contain
return nil return nil
} }
container, err := getByLabel(client, ID, containerCfg.Id) container, err := getByLabel(client, config.ID, containerCfg.Id)
if err != nil || container == nil { if err != nil || container == nil {
return err return err
} }
@ -360,7 +404,7 @@ func appendVolumesFrom(client *dockerClient.Client, containerCfg *config.Contain
return nil return nil
} }
func (c *Container) start(create_only, wait bool) *Container { func (c *Container) start(createOnly, wait bool) *Container {
c.Lookup() c.Lookup()
c.Stage() c.Stage()
@ -378,6 +422,7 @@ func (c *Container) start(create_only, wait bool) *Container {
opts, err := c.getCreateOpts(client) opts, err := c.getCreateOpts(client)
if err != nil { if err != nil {
log.Errorf("Failed to create container create options: %v", err)
return c.returnErr(err) return c.returnErr(err)
} }
@ -420,7 +465,7 @@ func (c *Container) start(create_only, wait bool) *Container {
hostConfig = opts.HostConfig hostConfig = opts.HostConfig
} }
if create_only { if createOnly {
return c return c
} }
@ -439,12 +484,17 @@ func (c *Container) start(create_only, wait bool) *Container {
err = client.StartContainer(c.Container.ID, hostConfig) err = client.StartContainer(c.Container.ID, hostConfig)
if err != nil { if err != nil {
log.Errorf("Error from Docker %s", err)
return c.returnErr(err) return c.returnErr(err)
} }
} }
if !c.detach && wait { if !c.detach && wait {
_, c.Err = client.WaitContainer(c.Container.ID) var exitCode int
exitCode, c.Err = client.WaitContainer(c.Container.ID)
if exitCode != 0 {
c.Err = errors.New(fmt.Sprintf("Container %s exited with code %d", c.Name, exitCode))
}
return c return c
} }

69
docker/factory.go Normal file
View File

@ -0,0 +1,69 @@
package docker
import (
log "github.com/Sirupsen/logrus"
"github.com/rancherio/os/config"
"github.com/rancherio/rancher-compose/project"
)
type ContainerFactory struct {
}
type containerBasedService struct {
name string
project *project.Project
container *Container
serviceConfig *project.ServiceConfig
cfg *config.Config
}
func (c *containerBasedService) Up() error {
container := c.container
containerCfg := c.container.ContainerCfg
if containerCfg.CreateOnly {
container.Create()
c.project.Notify(project.CONTAINER_CREATED, c, map[string]string{
project.CONTAINER_ID: container.Container.ID,
})
} else {
container.StartAndWait()
c.project.Notify(project.CONTAINER_STARTED, c, map[string]string{
project.CONTAINER_ID: container.Container.ID,
})
}
if container.Err != nil {
log.Errorf("Failed to run %v: %v", containerCfg.Id, container.Err)
}
if container.Err == nil && containerCfg.ReloadConfig {
return project.ErrRestart
}
return container.Err
}
func (c *containerBasedService) Config() *project.ServiceConfig {
return c.serviceConfig
}
func (c *containerBasedService) Name() string {
return c.name
}
func (c *ContainerFactory) Create(project *project.Project, name string, serviceConfig *project.ServiceConfig) (project.Service, error) {
container := NewContainerFromService(config.DOCKER_SYSTEM_HOST, name, serviceConfig)
if container.Err != nil {
return nil, container.Err
}
return &containerBasedService{
name: name,
project: project,
container: container,
serviceConfig: serviceConfig,
}, nil
}

View File

@ -8,9 +8,10 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
"github.com/rancherio/os/util" "github.com/rancherio/os/util"
"github.com/rancherio/rancher-compose/project"
) )
func runBootstrapContainers(cfg *config.Config) error { func autoformat(cfg *config.Config) error {
if len(cfg.State.Autoformat) == 0 || util.ResolveDevice(cfg.State.Dev) != "" { if len(cfg.State.Autoformat) == 0 || util.ResolveDevice(cfg.State.Dev) != "" {
return nil return nil
} }
@ -54,24 +55,22 @@ outer:
if format != "" { if format != "" {
log.Infof("Auto formatting : %s", format) log.Infof("Auto formatting : %s", format)
return runContainersFrom("", cfg, append([]config.ContainerConfig{ return runServices("autoformat", cfg, map[string]*project.ServiceConfig{
{ "autoformat": {
Id: "auto-format", Net: "none",
Cmd: "--name auto-format " + Privileged: true,
"--rm " + Image: "autoformat",
"--net=none " + Command: format,
"--privileged " +
"autoformat " +
format,
}, },
}, cfg.BootstrapContainers...)) "udev": cfg.BootstrapContainers["udev"],
})
} }
return nil return nil
} }
func autoformat(cfg *config.Config) error { func runBootstrapContainers(cfg *config.Config) error {
return runContainersFrom("", cfg, cfg.BootstrapContainers) return runServices("bootstrap", cfg, cfg.BootstrapContainers)
} }
func startDocker(cfg *config.Config) (chan interface{}, error) { func startDocker(cfg *config.Config) (chan interface{}, error) {

View File

@ -10,6 +10,8 @@ import (
"github.com/rancherio/os/config" "github.com/rancherio/os/config"
"github.com/rancherio/os/docker" "github.com/rancherio/os/docker"
"github.com/rancherio/os/util" "github.com/rancherio/os/util"
"github.com/rancherio/rancher-compose/project"
) )
func importImage(client *dockerClient.Client, name, fileName string) error { func importImage(client *dockerClient.Client, name, fileName string) error {
@ -107,50 +109,63 @@ func loadImages(cfg *config.Config) error {
return nil return nil
} }
func runContainersFrom(startFrom string, cfg *config.Config, containerConfigs []config.ContainerConfig) error { func runServices(name string, cfg *config.Config, configs map[string]*project.ServiceConfig) error {
foundStart := false project := project.NewProject(name, &docker.ContainerFactory{})
enabled := make(map[string]bool)
for i, containerConfig := range containerConfigs { for name, serviceConfig := range configs {
container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &containerConfig) if err := project.AddConfig(name, serviceConfig); err != nil {
log.Infof("Failed loading service %s", name)
if util.Contains(cfg.Disable, containerConfig.Id) {
log.Infof("%s is disabled : %v", containerConfig.Id, cfg.Disable)
continue
}
if foundStart || startFrom == "" {
if containerConfig.CreateOnly {
log.Infof("Creating [%d/%d] %s", i+1, len(containerConfigs), containerConfig.Id)
container.Create()
} else {
log.Infof("Running [%d/%d] %s", i+1, len(containerConfigs), containerConfig.Id)
container.StartAndWait()
}
if container.Err != nil {
log.Errorf("Failed to run %v: %v", containerConfig.Id, container.Err)
}
if containerConfig.ReloadConfig {
log.Info("Reloading configuration")
err := cfg.Reload()
if err != nil {
return err
}
return runContainersFrom(containerConfig.Id, cfg, cfg.SystemContainers)
}
} else if startFrom == containerConfig.Id {
foundStart = true
} }
} }
return nil project.ReloadCallback = func() error {
err := cfg.Reload()
if err != nil {
return err
}
for _, addon := range cfg.EnabledAddons {
if _, ok := enabled[addon]; ok {
continue
}
if config, ok := cfg.Addons[addon]; ok {
for name, s := range config.SystemContainers {
if err := project.AddConfig(name, s); err != nil {
log.Errorf("Failed to load %s : %v", name, err)
}
}
} else {
bytes, err := util.LoadResource(addon)
if err != nil {
log.Errorf("Failed to load %s : %v", addon, err)
continue
}
err = project.Load(bytes)
if err != nil {
log.Errorf("Failed to load %s : %v", addon, err)
continue
}
}
enabled[addon] = true
}
return nil
}
err := project.ReloadCallback()
if err != nil {
log.Errorf("Failed to reload %s : %v", name, err)
return err
}
return project.Up()
} }
func runContainers(cfg *config.Config) error { func runContainers(cfg *config.Config) error {
return runContainersFrom("", cfg, cfg.SystemContainers) return runServices("system-init", cfg, cfg.SystemContainers)
} }
func tailConsole(cfg *config.Config) error { func tailConsole(cfg *config.Config) error {
@ -163,29 +178,26 @@ func tailConsole(cfg *config.Config) error {
return err return err
} }
for _, container := range cfg.SystemContainers { console, ok := cfg.SystemContainers[config.CONSOLE_CONTAINER]
if container.Id != config.CONSOLE_CONTAINER { if !ok {
continue log.Error("Console not found")
} return nil
c := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &container).Lookup()
if c.Err != nil {
continue
}
log.Infof("Tailing console : %s", c.Name)
return client.Logs(dockerClient.LogsOptions{
Container: c.Name,
Stdout: true,
Stderr: true,
Follow: true,
OutputStream: os.Stdout,
ErrorStream: os.Stderr,
})
} }
log.Error("Console not found") c := docker.NewContainerFromService(config.DOCKER_SYSTEM_HOST, config.CONSOLE_CONTAINER, console)
return nil if c.Err != nil {
return c.Err
}
log.Infof("Tailing console : %s", c.Name)
return client.Logs(dockerClient.LogsOptions{
Container: c.Name,
Stdout: true,
Stderr: true,
Follow: true,
OutputStream: os.Stdout,
ErrorStream: os.Stderr,
})
} }
func SysInit() error { func SysInit() error {
@ -201,6 +213,10 @@ func SysInit() error {
syscall.Sync() syscall.Sync()
return nil return nil
}, },
func(cfg *config.Config) error {
log.Info("RancherOS booted")
return nil
},
tailConsole, tailConsole,
} }

View File

@ -4,9 +4,12 @@ import (
"archive/tar" "archive/tar"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math/rand" "math/rand"
"net/http"
"os" "os"
"path" "path"
"strings"
"syscall" "syscall"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
@ -182,3 +185,16 @@ func MergeMaps(left, right map[interface{}]interface{}) {
} }
} }
} }
func LoadResource(location string) ([]byte, error) {
if strings.HasPrefix(location, "http:/") || strings.HasPrefix(location, "https:/") {
resp, err := http.Get(location)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
} else {
return ioutil.ReadFile(location)
}
}