diff --git a/cmd/console/console.go b/cmd/console/console.go index c8547286..76153c03 100644 --- a/cmd/console/console.go +++ b/cmd/console/console.go @@ -105,10 +105,10 @@ func Main() { cloudinitexecute.ApplyConsole(cfg) - if err := runScript(config.CloudConfigScriptFile); err != nil { + if err := util.RunScript(config.CloudConfigScriptFile); err != nil { log.Error(err) } - if err := runScript(startScript); err != nil { + if err := util.RunScript(startScript); err != nil { log.Error(err) } @@ -116,7 +116,7 @@ func Main() { log.Error(err) } - if err := runScript("/etc/rc.local"); err != nil { + if err := util.RunScript("/etc/rc.local"); err != nil { log.Error(err) } @@ -282,29 +282,3 @@ func setupSSH(cfg *config.CloudConfig) error { return os.MkdirAll("/var/run/sshd", 0644) } - -func runScript(path string) error { - if !util.ExistsAndExecutable(path) { - return nil - } - - script, err := os.Open(path) - if err != nil { - return err - } - - magic := make([]byte, 2) - if _, err = script.Read(magic); err != nil { - return err - } - - cmd := exec.Command("/bin/sh", path) - if string(magic) == "#!" { - cmd = exec.Command(path) - } - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - return cmd.Run() -} diff --git a/cmd/control/bootstrap.go b/cmd/control/bootstrap.go new file mode 100644 index 00000000..5051a506 --- /dev/null +++ b/cmd/control/bootstrap.go @@ -0,0 +1,107 @@ +package control + +import ( + "io/ioutil" + "os" + "os/exec" + "strings" + "time" + + "github.com/codegangsta/cli" + + log "github.com/Sirupsen/logrus" + "github.com/rancher/os/config" + "github.com/rancher/os/util" +) + +func bootstrapAction(c *cli.Context) error { + if err := UdevSettle(); err != nil { + log.Errorf("Failed to run udev settle: %v", err) + } + + cfg := config.LoadConfig() + + if cfg.Rancher.State.MdadmScan { + if err := mdadmScan(); err != nil { + log.Errorf("Failed to run mdadm scan: %v", err) + } + } + + stateScript := cfg.Rancher.State.Script + if stateScript != "" { + if err := runStateScript(stateScript); err != nil { + log.Errorf("Failed to run state script: %v", err) + } + } + + if cfg.Rancher.State.Dev != "" && cfg.Rancher.State.Wait { + waitForRoot(cfg) + } + + autoformatDevices := cfg.Rancher.State.Autoformat + if len(autoformatDevices) > 0 { + if err := autoformat(autoformatDevices); err != nil { + log.Errorf("Failed to run autoformat: %v", err) + } + } + + if err := UdevSettle(); err != nil { + log.Errorf("Failed to run udev settle: %v", err) + } + + return nil +} + +func mdadmScan() error { + cmd := exec.Command("mdadm", "--assemble", "--scan") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func runStateScript(script string) error { + f, err := ioutil.TempFile("", "") + if err != nil { + return err + } + if _, err := f.WriteString(script); err != nil { + return err + } + if err := f.Chmod(os.ModePerm); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + return util.RunScript(f.Name()) +} + +func waitForRoot(cfg *config.CloudConfig) { + var dev string + for i := 0; i < 30; i++ { + dev = util.ResolveDevice(cfg.Rancher.State.Dev) + if dev != "" { + break + } + time.Sleep(time.Millisecond * 1000) + } + if dev == "" { + return + } + for i := 0; i < 30; i++ { + if _, err := os.Stat(dev); err == nil { + break + } + time.Sleep(time.Millisecond * 1000) + } +} + +func autoformat(autoformatDevices []string) error { + cmd := exec.Command("/usr/sbin/auto-format.sh") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = []string{ + "AUTOFORMAT=" + strings.Join(autoformatDevices, " "), + } + return cmd.Run() +} diff --git a/cmd/control/cli.go b/cmd/control/cli.go index e09bb52f..13544c45 100644 --- a/cmd/control/cli.go +++ b/cmd/control/cli.go @@ -24,6 +24,12 @@ func Main() { } app.Commands = []cli.Command{ + { + Name: "bootstrap", + HideHelp: true, + SkipFlagParsing: true, + Action: bootstrapAction, + }, { Name: "config", ShortName: "c", @@ -72,12 +78,24 @@ func Main() { HideHelp: true, Subcommands: osSubcommands(), }, + { + Name: "preload-images", + HideHelp: true, + SkipFlagParsing: true, + Action: preloadImagesAction, + }, { Name: "tls", Usage: "setup tls configuration", HideHelp: true, Subcommands: tlsConfCommands(), }, + { + Name: "udev-settle", + HideHelp: true, + SkipFlagParsing: true, + Action: udevSettleAction, + }, installCommand, selinuxCommand(), } diff --git a/cmd/control/config.go b/cmd/control/config.go index 01b45b69..69f9cb51 100644 --- a/cmd/control/config.go +++ b/cmd/control/config.go @@ -85,9 +85,6 @@ func imagesFromConfig(cfg *config.CloudConfig) []string { for _, service := range cfg.Rancher.BootstrapContainers { imagesMap[service.Image] = 1 } - for _, service := range cfg.Rancher.Autoformat { - imagesMap[service.Image] = 1 - } for _, service := range cfg.Rancher.Services { imagesMap[service.Image] = 1 } diff --git a/cmd/control/preload.go b/cmd/control/preload.go new file mode 100644 index 00000000..ddf3bea7 --- /dev/null +++ b/cmd/control/preload.go @@ -0,0 +1,106 @@ +package control + +import ( + "compress/gzip" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "regexp" + "strings" + + "github.com/codegangsta/cli" + + log "github.com/Sirupsen/logrus" + dockerClient "github.com/docker/engine-api/client" + "github.com/rancher/os/docker" +) + +const ( + userImagesPreloadDirectory = "/var/lib/rancher/preload/docker" +) + +func preloadImagesAction(c *cli.Context) error { + return PreloadImages(docker.NewDefaultClient, userImagesPreloadDirectory) +} + +func shouldLoad(file string) bool { + if strings.HasSuffix(file, ".done") { + return false + } + if _, err := os.Stat(fmt.Sprintf("%s.done", file)); err == nil { + return false + } + return true +} + +func PreloadImages(clientFactory func() (dockerClient.APIClient, error), imagesDir string) error { + var client dockerClient.APIClient + clientInitialized := false + + if _, err := os.Stat(imagesDir); os.IsNotExist(err) { + if err = os.MkdirAll(imagesDir, 0755); err != nil { + return err + } + } else if err != nil { + return err + } + + files, err := ioutil.ReadDir(imagesDir) + if err != nil { + return err + } + + for _, file := range files { + filename := path.Join(imagesDir, file.Name()) + if !shouldLoad(filename) { + continue + } + + image, err := os.Open(filename) + if err != nil { + return err + } + var imageReader io.Reader + imageReader = image + match, err := regexp.MatchString(".t?gz$", file.Name()) + if err != nil { + return err + } + if match { + imageReader, err = gzip.NewReader(image) + if err != nil { + return err + } + } + + if !clientInitialized { + client, err = clientFactory() + if err != nil { + return err + } + clientInitialized = true + } + + log.Infof("Loading image %s", filename) + if _, err = client.ImageLoad(context.Background(), imageReader, false); err != nil { + return err + } + + if err = image.Close(); err != nil { + return err + } + + doneStamp, err := os.Create(fmt.Sprintf("%s.done", filename)) + if err != nil { + return err + } + if err = doneStamp.Close(); err != nil { + return err + } + } + + return nil +} diff --git a/cmd/control/udevsettle.go b/cmd/control/udevsettle.go new file mode 100644 index 00000000..271c66f3 --- /dev/null +++ b/cmd/control/udevsettle.go @@ -0,0 +1,36 @@ +package control + +import ( + "os" + "os/exec" + + log "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" +) + +func udevSettleAction(c *cli.Context) { + if err := UdevSettle(); err != nil { + log.Fatal(err) + } +} + +func UdevSettle() error { + cmd := exec.Command("udevd", "--daemon") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + cmd = exec.Command("udevadm", "trigger", "--action=add") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + cmd = exec.Command("udevadm", "settle") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} diff --git a/config/disk.go b/config/disk.go index dbfd6d5d..92691c4a 100644 --- a/config/disk.go +++ b/config/disk.go @@ -176,9 +176,6 @@ func amendNils(c *CloudConfig) *CloudConfig { if t.Rancher.Environment == nil { t.Rancher.Environment = map[string]string{} } - if t.Rancher.Autoformat == nil { - t.Rancher.Autoformat = map[string]*composeConfig.ServiceConfigV1{} - } if t.Rancher.BootstrapContainers == nil { t.Rancher.BootstrapContainers = map[string]*composeConfig.ServiceConfigV1{} } @@ -199,7 +196,6 @@ func amendNils(c *CloudConfig) *CloudConfig { func amendContainerNames(c *CloudConfig) *CloudConfig { for _, scm := range []map[string]*composeConfig.ServiceConfigV1{ - c.Rancher.Autoformat, c.Rancher.BootstrapContainers, c.Rancher.Services, } { diff --git a/config/types.go b/config/types.go index 12504af8..5de8a480 100644 --- a/config/types.go +++ b/config/types.go @@ -102,7 +102,6 @@ type RancherConfig struct { Environment map[string]string `yaml:"environment,omitempty"` Services map[string]*composeConfig.ServiceConfigV1 `yaml:"services,omitempty"` BootstrapContainers map[string]*composeConfig.ServiceConfigV1 `yaml:"bootstrap,omitempty"` - Autoformat map[string]*composeConfig.ServiceConfigV1 `yaml:"autoformat,omitempty"` BootstrapDocker DockerConfig `yaml:"bootstrap_docker,omitempty"` CloudInit CloudInit `yaml:"cloud_init,omitempty"` Debug bool `yaml:"debug,omitempty"` diff --git a/images/02-autoformat/Dockerfile b/images/02-bootstrap/Dockerfile similarity index 62% rename from images/02-autoformat/Dockerfile rename to images/02-bootstrap/Dockerfile index 9424c9ca..554d121e 100644 --- a/images/02-autoformat/Dockerfile +++ b/images/02-bootstrap/Dockerfile @@ -1,4 +1,3 @@ FROM rancher/os-base COPY auto-format.sh /usr/sbin/ COPY od-1m0 / -ENTRYPOINT ["/usr/sbin/auto-format.sh"] diff --git a/images/02-autoformat/auto-format.sh b/images/02-bootstrap/auto-format.sh similarity index 100% rename from images/02-autoformat/auto-format.sh rename to images/02-bootstrap/auto-format.sh diff --git a/images/02-autoformat/od-1m0 b/images/02-bootstrap/od-1m0 similarity index 100% rename from images/02-autoformat/od-1m0 rename to images/02-bootstrap/od-1m0 diff --git a/images/02-preload/Dockerfile b/images/02-preload/Dockerfile deleted file mode 100644 index afc02c49..00000000 --- a/images/02-preload/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM rancher/os-base -RUN ln -sf /var/lib/rancher/engine/docker /usr/bin/docker -COPY preload.sh / -CMD ["/preload.sh"] diff --git a/images/02-preload/preload.sh b/images/02-preload/preload.sh deleted file mode 100755 index 93526be2..00000000 --- a/images/02-preload/preload.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -set -e - -BASE=${1:-${PRELOAD_DIR}} -BASE=${BASE:-/mnt/preload} - -if [ "${SYSTEM_IMAGES}" = "true" ]; then - docker_bin=system-docker -else - docker_bin=docker -fi - -should_load() { - file=${1} - if [[ ${file} =~ \.done$ ]]; then echo false - elif [ -f ${file} ]; then - if [[ ${file} -nt ${file}.done ]]; then echo true - else echo false - fi - else echo false - fi -} - -if [ -d ${BASE} ]; then - echo Preloading docker images from ${BASE}... - - for file in $(ls ${BASE}); do - path=${BASE}/${file} - loading=$(should_load ${path}) - if [ ${loading} == "true" ]; then - CAT="cat ${path}" - if [[ ${file} =~ \.t?gz$ ]]; then CAT="${CAT} | gunzip"; fi - if [[ ${file} =~ \.t?xz$ ]]; then CAT="${CAT} | unxz"; fi - wait-for-docker - CAT="${CAT} | ${docker_bin} load" - echo loading from ${path} - eval ${CAT} || : - touch ${path}.done || : - fi - done - - echo Done. -else - echo Can not preload images from ${BASE}: not a dir or does not exist. -fi - diff --git a/images/02-statescript/Dockerfile b/images/02-statescript/Dockerfile deleted file mode 100644 index 46dacbac..00000000 --- a/images/02-statescript/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM rancher/os-base -COPY state.sh /usr/sbin/ -CMD ["/usr/sbin/state.sh"] diff --git a/images/02-statescript/state.sh b/images/02-statescript/state.sh deleted file mode 100755 index bc11050c..00000000 --- a/images/02-statescript/state.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -x - -if [ "$(ros config get rancher.state.mdadm_scan)" = "true" ]; then - mdadm --assemble --scan -fi - -ros config get rancher.state.script > config.sh -if [ -s config.sh ]; then - chmod +x config.sh - exec ./config.sh -fi diff --git a/images/02-udev/Dockerfile b/images/02-udev/Dockerfile deleted file mode 100644 index 6f8de398..00000000 --- a/images/02-udev/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM rancher/os-base -COPY udev.sh / -CMD ["/udev.sh"] diff --git a/images/02-udev/udev.sh b/images/02-udev/udev.sh deleted file mode 100755 index b91a9cc9..00000000 --- a/images/02-udev/udev.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -if [ "$DAEMON" = true ]; then - exec udevd -fi - -udevd --daemon -udevadm trigger --action=add -udevadm settle - -dev=$(ros config get rancher.state.dev) -wait=$(ros config get rancher.state.wait) -if [ "$BOOTSTRAP" != true ] || [ "$dev" == "" ] || [ "$wait" != "true" ]; then - exit -fi - -for i in `seq 1 30`; do - drive=$(ros dev $dev) - if [ "$drive" != "" ]; then - break - fi - sleep 1 -done -drive=$(ros dev $dev) -if [ "$drive" = "" ]; then - exit -fi -for i in `seq 1 30`; do - if [ -e $drive ]; then - break - fi - sleep 1 -done diff --git a/init/bootstrap.go b/init/bootstrap.go index 9b4f35f1..487cf94d 100644 --- a/init/bootstrap.go +++ b/init/bootstrap.go @@ -3,8 +3,6 @@ package init import ( "syscall" - "strings" - log "github.com/Sirupsen/logrus" "github.com/rancher/docker-from-scratch" "github.com/rancher/os/compose" @@ -12,20 +10,11 @@ import ( "github.com/rancher/os/util" ) -func autoformat(cfg *config.CloudConfig) (*config.CloudConfig, error) { +func bootstrapServices(cfg *config.CloudConfig) (*config.CloudConfig, error) { if len(cfg.Rancher.State.Autoformat) == 0 || util.ResolveDevice(cfg.Rancher.State.Dev) != "" { return cfg, nil } - AUTOFORMAT := "AUTOFORMAT=" + strings.Join(cfg.Rancher.State.Autoformat, " ") - t := *cfg - t.Rancher.Autoformat["autoformat"].Environment = []string{AUTOFORMAT} - log.Info("Running Autoformat services") - _, err := compose.RunServiceSet("autoformat", &t, t.Rancher.Autoformat) - return &t, err -} - -func runBootstrapContainers(cfg *config.CloudConfig) (*config.CloudConfig, error) { - log.Info("Running Bootstrap services") + log.Info("Running Bootstrap") _, err := compose.RunServiceSet("bootstrap", cfg, cfg.Rancher.BootstrapContainers) return cfg, err } @@ -70,7 +59,6 @@ func bootstrap(cfg *config.CloudConfig) error { _, err = config.ChainCfgFuncs(cfg, loadImages, - runBootstrapContainers, - autoformat) + bootstrapServices) return err } diff --git a/init/sysinit.go b/init/sysinit.go index 0dfa96cb..9e350f78 100644 --- a/init/sysinit.go +++ b/init/sysinit.go @@ -9,11 +9,16 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libcompose/project/options" + "github.com/rancher/os/cmd/control" "github.com/rancher/os/compose" "github.com/rancher/os/config" "github.com/rancher/os/docker" ) +const ( + systemImagesPreloadDirectory = "/var/lib/rancher/preload/system-docker" +) + func hasImage(name string) bool { stamp := path.Join(STATE, name) if _, err := os.Stat(stamp); os.IsNotExist(err) { @@ -91,6 +96,10 @@ func loadImages(cfg *config.CloudConfig) (*config.CloudConfig, error) { func SysInit() error { cfg := config.LoadConfig() + if err := control.PreloadImages(docker.NewSystemClient, systemImagesPreloadDirectory); err != nil { + log.Errorf("Failed to preload System Docker images: %v", err) + } + _, err := config.ChainCfgFuncs(cfg, loadImages, func(cfg *config.CloudConfig) (*config.CloudConfig, error) { diff --git a/os-config.tpl.yml b/os-config.tpl.yml index ed175b5b..7109f299 100644 --- a/os-config.tpl.yml +++ b/os-config.tpl.yml @@ -15,15 +15,14 @@ rancher: dns: nameservers: [8.8.8.8, 8.8.4.4] bootstrap: - state-script: - image: {{.OS_REPO}}/os-statescript:{{.VERSION}}{{.SUFFIX}} + bootstrap: + image: {{.OS_REPO}}/os-bootstrap:{{.VERSION}}{{.SUFFIX}} + command: ros bootstrap labels: io.rancher.os.detach: "false" io.rancher.os.scope: system - io.rancher.os.after: udev-bootstrap log_driver: json-file - net: host - uts: host + net: none privileged: true volumes: - /dev:/host/dev @@ -31,46 +30,6 @@ rancher: - /lib/firmware:/lib/firmware - /usr/bin/ros:/usr/bin/ros:ro - /usr/share/ros:/usr/share/ros:ro - udev-bootstrap: - image: {{.OS_REPO}}/os-udev:{{.VERSION}}{{.SUFFIX}} - environment: - - BOOTSTRAP=true - 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 - - /usr/bin/ros:/usr/bin/ros:ro - autoformat: - autoformat: - image: {{.OS_REPO}}/os-autoformat:{{.VERSION}}{{.SUFFIX}} - labels: - io.rancher.os.detach: "false" - io.rancher.os.scope: system - log_driver: json-file - net: none - privileged: true - udev-autoformat: - image: {{.OS_REPO}}/os-udev:{{.VERSION}}{{.SUFFIX}} - labels: - io.rancher.os.detach: "false" - io.rancher.os.scope: system - io.rancher.os.after: autoformat - log_driver: json-file - net: host - uts: host - privileged: true - volumes: - - /dev:/host/dev - - /lib/modules:/lib/modules - - /lib/firmware:/lib/firmware - - /usr/bin/ros:/usr/bin/ros:ro bootstrap_docker: bridge: none storage_driver: overlay @@ -157,7 +116,7 @@ rancher: io.rancher.os.detach: "false" io.rancher.os.reloadconfig: "true" io.rancher.os.scope: system - io.rancher.os.after: udev,preload-system-images + io.rancher.os.after: udev net: host uts: host privileged: true @@ -263,34 +222,14 @@ rancher: volumes_from: - command-volumes - system-volumes - preload-system-images: - image: {{.OS_REPO}}/os-preload:{{.VERSION}}{{.SUFFIX}} - environment: - - SYSTEM_IMAGES=true - labels: - io.rancher.os.detach: "false" - io.rancher.os.scope: system - net: host - privileged: true - volumes: - - /var/run/system-docker.sock:/var/run/docker.sock - - /var/lib/rancher/preload/system-docker:/mnt/preload - - /usr/bin/ros:/usr/sbin/wait-for-docker:ro - volumes_from: - - command-volumes - - system-volumes preload-user-images: - image: {{.OS_REPO}}/os-preload:{{.VERSION}}{{.SUFFIX}} + image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}} + command: ros preload-images labels: io.rancher.os.detach: "false" io.rancher.os.scope: system io.rancher.os.after: console - net: host privileged: true - volumes: - - /var/run/:/var/run/ - - /var/lib/rancher/preload/docker:/mnt/preload - - /usr/bin/ros:/usr/sbin/wait-for-docker:ro volumes_from: - command-volumes - system-volumes @@ -333,7 +272,8 @@ rancher: - /var/log:/var/log - /var/run:/var/run udev-cold: - image: {{.OS_REPO}}/os-udev:{{.VERSION}}{{.SUFFIX}} + image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}} + command: ros udev-settle labels: io.rancher.os.detach: "false" io.rancher.os.scope: system @@ -344,9 +284,8 @@ rancher: - command-volumes - system-volumes udev: - image: {{.OS_REPO}}/os-udev:{{.VERSION}}{{.SUFFIX}} - environment: - - DAEMON=true + image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}} + command: udevd labels: io.rancher.os.detach: "true" io.rancher.os.scope: system diff --git a/tests/preload_test.go b/tests/preload_test.go index 960399ee..66a6714c 100644 --- a/tests/preload_test.go +++ b/tests/preload_test.go @@ -11,6 +11,7 @@ func (s *QemuSuite) TestPreload(c *C) { s.CheckCall(c, ` docker pull busybox sudo docker save -o /var/lib/rancher/preload/system-docker/busybox.tar busybox +sudo gzip /var/lib/rancher/preload/system-docker/busybox.tar sudo system-docker pull alpine sudo system-docker save -o /var/lib/rancher/preload/docker/alpine.tar alpine`) diff --git a/util/util.go b/util/util.go index 90cddc88..a58ef358 100644 --- a/util/util.go +++ b/util/util.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path" "strings" @@ -241,3 +242,29 @@ func ExistsAndExecutable(path string) bool { mode := info.Mode().Perm() return mode&os.ModePerm != 0 } + +func RunScript(path string) error { + if !ExistsAndExecutable(path) { + return nil + } + + script, err := os.Open(path) + if err != nil { + return err + } + + magic := make([]byte, 2) + if _, err = script.Read(magic); err != nil { + return err + } + + cmd := exec.Command("/bin/sh", path) + if string(magic) == "#!" { + cmd = exec.Command(path) + } + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +}