diff --git a/cmd/sysinit/sysinit.go b/cmd/sysinit/sysinit.go index 3a4bbe11..f0922f9a 100644 --- a/cmd/sysinit/sysinit.go +++ b/cmd/sysinit/sysinit.go @@ -1,225 +1,12 @@ package sysinit import ( - "os" - "os/exec" - "path" - "syscall" - log "github.com/Sirupsen/logrus" - dockerClient "github.com/fsouza/go-dockerclient" - "github.com/rancherio/os/config" - "github.com/rancherio/os/docker" initPkg "github.com/rancherio/os/init" - "github.com/rancherio/os/util" ) func Main() { - if err := sysInit(); err != nil { + if err := initPkg.SysInit(); err != nil { log.Fatal(err) } } - -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(initPkg.STATE, name) - if _, err := os.Stat(stamp); os.IsNotExist(err) { - return false - } - return true -} - -func findImages(cfg *config.Config) ([]string, error) { - log.Debugf("Looking for images at %s", config.IMAGES_PATH) - - result := []string{} - - dir, err := os.Open(config.IMAGES_PATH) - if os.IsNotExist(err) { - log.Debugf("Not loading images, %s does not exist") - return result, nil - } - if err != nil { - return nil, err - } - - defer dir.Close() - - files, err := dir.Readdirnames(0) - if err != nil { - return nil, err - } - - for _, fileName := range files { - if ok, _ := path.Match(config.IMAGES_PATTERN, fileName); ok { - log.Debugf("Found %s", fileName) - result = append(result, fileName) - } - } - - return result, nil -} - -func loadImages(cfg *config.Config) error { - images, err := findImages(cfg) - if err != nil || len(images) == 0 { - return err - } - - client, err := docker.NewSystemClient() - if err != nil { - return err - } - - for _, image := range images { - if hasImage(image) { - continue - } - - inputFileName := path.Join(config.IMAGES_PATH, image) - input, err := os.Open(inputFileName) - if err != nil { - return err - } - - defer input.Close() - - log.Infof("Loading images from %s", inputFileName) - err = client.LoadImage(dockerClient.LoadImageOptions{ - InputStream: input, - }) - log.Infof("Done loading images from %s", inputFileName) - - if err != nil { - return err - } - } - - return nil -} - -func runContainersFrom(startFrom string, cfg *config.Config, containerConfigs []config.ContainerConfig) error { - foundStart := false - - for i, containerConfig := range containerConfigs { - container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &containerConfig) - - if util.Contains(cfg.Disable, containerConfig.Id) { - log.Infof("%s is disabled : %v", containerConfig.Id, cfg.Disable) - continue - } - - //if containerConfig.Id == config.CONSOLE_CONTAINER { - // if util.IsRunningInTty() { - // container.Config.Tty = true - // container.Config.AttachStdin = true - // container.Config.AttachStdout = true - // container.Config.AttachStderr = true - // } - //} - - if foundStart || startFrom == "" { - 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 -} - -func runContainers(cfg *config.Config) error { - containerConfigs := cfg.SystemContainers - if cfg.Rescue { - log.Debug("Running rescue container") - containerConfigs = []config.ContainerConfig{*cfg.RescueContainer} - } - - return runContainersFrom("", cfg, containerConfigs) -} - -func tailConsole(cfg *config.Config) error { - if !cfg.Console.Tail { - return nil - } - - client, err := docker.NewSystemClient() - if err != nil { - return err - } - - for _, container := range cfg.SystemContainers { - if container.Id != config.CONSOLE_CONTAINER { - continue - } - - 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") - return nil -} - -func sysInit() error { - cfg, err := config.LoadConfig() - if err != nil { - return err - } - - initFuncs := []config.InitFunc{ - loadImages, - runContainers, - func(cfg *config.Config) error { - syscall.Sync() - return nil - }, - tailConsole, - } - - return config.RunInitFuncs(cfg, initFuncs) -} diff --git a/init/bootstrap.go b/init/bootstrap.go new file mode 100644 index 00000000..bed48765 --- /dev/null +++ b/init/bootstrap.go @@ -0,0 +1,129 @@ +package init + +import ( + "os" + "os/exec" + "syscall" + + log "github.com/Sirupsen/logrus" + "github.com/rancherio/os/config" + "github.com/rancherio/os/util" +) + +func runBootstrapContainers(cfg *config.Config) error { + if len(cfg.State.Autoformat) == 0 || util.ResolveDevice(cfg.State.Dev) != "" { + return nil + } + + var format string + +outer: + for _, dev := range cfg.State.Autoformat { + log.Infof("Checking %s to auto-format", dev) + if _, err := os.Stat(dev); os.IsNotExist(err) { + continue + } + + f, err := os.Open(dev) + if err != nil { + return err + } + defer f.Close() + + buffer := make([]byte, 1048576, 1048576) + c, err := f.Read(buffer) + if err != nil { + return err + } + + if c != 1048576 { + log.Infof("%s not right size", dev) + continue + } + + for _, b := range buffer { + if b != 0 { + log.Infof("%s not empty", dev) + continue outer + } + } + + format = dev + break + } + + if format != "" { + log.Infof("Auto formatting : %s", format) + return runContainersFrom("", cfg, append([]config.ContainerConfig{ + { + Id: "auto-format", + Cmd: "--name auto-format " + + "--rm " + + "--net=none " + + "--privileged " + + "autoformat " + + format, + }, + }, cfg.BootstrapContainers...)) + } + + return nil +} + +func autoformat(cfg *config.Config) error { + return runContainersFrom("", cfg, cfg.BootstrapContainers) +} + +func startDocker(cfg *config.Config) (chan interface{}, error) { + for _, d := range []string{config.DOCKER_SYSTEM_HOST, "/var/run"} { + err := os.MkdirAll(d, 0700) + if err != nil { + return nil, err + } + } + + cmd := exec.Command(cfg.SystemDocker.Args[0], cfg.SystemDocker.Args[1:]...) + if cfg.Debug { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + err := cmd.Start() + if err != nil { + return nil, err + } + + c := make(chan interface{}) + go func() { + <-c + cmd.Process.Signal(syscall.SIGTERM) + cmd.Wait() + c <- struct{}{} + }() + + return c, nil +} + +func stopDocker(c chan interface{}) error { + c <- struct{}{} + <-c + + return os.RemoveAll(config.DOCKER_SYSTEM_HOME) +} + +func bootstrap(cfg *config.Config) error { + log.Info("Starting bootstrap") + c, err := startDocker(cfg) + if err != nil { + return err + } + + initFuncs := []config.InitFunc{ + loadImages, + runBootstrapContainers, + autoformat, + } + + defer stopDocker(c) + + return config.RunInitFuncs(cfg, initFuncs) +} diff --git a/init/init.go b/init/init.go index 38d5df7c..be2a09ad 100644 --- a/init/init.go +++ b/init/init.go @@ -27,6 +27,9 @@ var ( } postDirs []string = []string{ "/var/log", + "/var/lib/rancher/state/docker", + "/var/lib/rancher/state/home", + "/var/lib/rancher/state/opt", } mounts [][]string = [][]string{ []string{"devtmpfs", "/dev", "devtmpfs", ""}, @@ -58,10 +61,15 @@ var ( "/sbin/modprobe": "/busybox", DOCKER: "/docker", SYSINIT: "/init", + "/home": "/var/lib/rancher/state/home", + "/opt": "/var/lib/rancher/state/opt", + } + postSymlinks map[string]string = map[string]string{ + "/var/lib/docker": "/var/lib/rancher/state/docker", } ) -func createSymlinks(cfg *config.Config) error { +func createSymlinks(cfg *config.Config, symlinks map[string]string) error { log.Debug("Creating symlinking") for dest, src := range symlinks { if _, err := os.Stat(dest); os.IsNotExist(err) { @@ -146,13 +154,28 @@ func setResolvConf(cfg *config.Config) error { defer f.Close() - for _, dns := range cfg.Dns { + for _, dns := range cfg.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, " ") + if search != "" { + content := fmt.Sprintf("search %s\n", search) + if _, err = f.Write([]byte(content)); err != nil { + return err + } + } + + if cfg.Network.Dns.Domain != "" { + content := fmt.Sprintf("domain %s\n", cfg.Network.Dns.Domain) + if _, err = f.Write([]byte(content)); err != nil { + return err + } + } + return nil } @@ -214,7 +237,7 @@ func execDocker(cfg *config.Config) error { } os.Stdin.Close() - return syscall.Exec(DOCKER, cfg.SystemDockerArgs, os.Environ()) + return syscall.Exec(DOCKER, cfg.SystemDocker.Args, os.Environ()) } func MainInit() { @@ -286,19 +309,26 @@ func RunInit() error { return err }, mountCgroups, - setResolvConf, - createSymlinks, + func(cfg *config.Config) error { + return createSymlinks(cfg, symlinks) + }, extractModules, loadModules, + setResolvConf, + bootstrap, mountState, + func(cfg *config.Config) error { + return cfg.Reload() + }, + setResolvConf, func(cfg *config.Config) error { return createDirs(postDirs...) }, func(cfg *config.Config) error { - return createMounts(postMounts...) + return createSymlinks(cfg, postSymlinks) }, func(cfg *config.Config) error { - return cfg.Reload() + return createMounts(postMounts...) }, remountRo, sysInit, diff --git a/init/sysinit.go b/init/sysinit.go new file mode 100644 index 00000000..5b1f2e80 --- /dev/null +++ b/init/sysinit.go @@ -0,0 +1,202 @@ +package init + +import ( + "os" + "path" + "syscall" + + log "github.com/Sirupsen/logrus" + dockerClient "github.com/fsouza/go-dockerclient" + "github.com/rancherio/os/config" + "github.com/rancherio/os/docker" + "github.com/rancherio/os/util" +) + +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) { + return false + } + return true +} + +func findImages(cfg *config.Config) ([]string, error) { + log.Debugf("Looking for images at %s", config.IMAGES_PATH) + + result := []string{} + + dir, err := os.Open(config.IMAGES_PATH) + if os.IsNotExist(err) { + log.Debugf("Not loading images, %s does not exist") + return result, nil + } + if err != nil { + return nil, err + } + + defer dir.Close() + + files, err := dir.Readdirnames(0) + if err != nil { + return nil, err + } + + for _, fileName := range files { + if ok, _ := path.Match(config.IMAGES_PATTERN, fileName); ok { + log.Debugf("Found %s", fileName) + result = append(result, fileName) + } + } + + return result, nil +} + +func loadImages(cfg *config.Config) error { + images, err := findImages(cfg) + if err != nil || len(images) == 0 { + return err + } + + client, err := docker.NewSystemClient() + if err != nil { + return err + } + + for _, image := range images { + if hasImage(image) { + continue + } + + inputFileName := path.Join(config.IMAGES_PATH, image) + input, err := os.Open(inputFileName) + if err != nil { + return err + } + + defer input.Close() + + log.Infof("Loading images from %s", inputFileName) + err = client.LoadImage(dockerClient.LoadImageOptions{ + InputStream: input, + }) + log.Infof("Done loading images from %s", inputFileName) + + if err != nil { + return err + } + } + + return nil +} + +func runContainersFrom(startFrom string, cfg *config.Config, containerConfigs []config.ContainerConfig) error { + foundStart := false + + for i, containerConfig := range containerConfigs { + container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &containerConfig) + + if util.Contains(cfg.Disable, containerConfig.Id) { + log.Infof("%s is disabled : %v", containerConfig.Id, cfg.Disable) + continue + } + + if foundStart || startFrom == "" { + 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 +} + +func runContainers(cfg *config.Config) error { + return runContainersFrom("", cfg, cfg.SystemContainers) +} + +func tailConsole(cfg *config.Config) error { + if !cfg.Console.Tail { + return nil + } + + client, err := docker.NewSystemClient() + if err != nil { + return err + } + + for _, container := range cfg.SystemContainers { + if container.Id != config.CONSOLE_CONTAINER { + continue + } + + 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") + return nil +} + +func SysInit() error { + cfg, err := config.LoadConfig() + if err != nil { + return err + } + + initFuncs := []config.InitFunc{ + loadImages, + runContainers, + func(cfg *config.Config) error { + syscall.Sync() + return nil + }, + tailConsole, + } + + return config.RunInitFuncs(cfg, initFuncs) +} diff --git a/scripts/dockerimages/10-autoformat b/scripts/dockerimages/10-autoformat new file mode 100644 index 00000000..6568c265 --- /dev/null +++ b/scripts/dockerimages/10-autoformat @@ -0,0 +1,3 @@ +FROM base +COPY scripts/dockerimages/scripts/auto-format.sh /usr/sbin/ +ENTRYPOINT ["/usr/sbin/auto-format.sh"] diff --git a/scripts/dockerimages/scripts/auto-format.sh b/scripts/dockerimages/scripts/auto-format.sh new file mode 100755 index 00000000..81e15a81 --- /dev/null +++ b/scripts/dockerimages/scripts/auto-format.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +if [ -n "$1" ]; then + exec mkfs.ext4 -L RANCHER_STATE $1 +fi