From 597a46c574b0885c2e128fd7c21c5a1607f5d899 Mon Sep 17 00:00:00 2001 From: sidharthamani Date: Tue, 17 Feb 2015 17:05:03 -0800 Subject: [PATCH 1/2] wire up tls config --- config/config.go | 8 +++++++ config/default.go | 5 ++++ main.go | 2 +- scripts/dockerimages/scripts/docker.sh | 33 ++++++++++++++++++++++++-- util/util.go | 2 +- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 189f022c..68a513bd 100644 --- a/config/config.go +++ b/config/config.go @@ -48,11 +48,19 @@ type Config struct { Rescue bool `yaml:"rescue,omitempty"` RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` State ConfigState `yaml:"state,omitempty"` + Userdocker UserDockerInfo `yaml:"userdocker,omitempty"` SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"` Modules []string `yaml:"modules,omitempty"` } +type UserDockerInfo struct { + UseTLS bool `yaml:"use_tls,omitempty"` + TLSServerCert string `yaml:"tls_server_cert"` + TLSServerKey string `yaml:"tls_server_key"` + TLSCACert string `yaml:"tls_ca_cert"` +} + type ConfigState struct { FsType string `yaml:"fstype"` Dev string `yaml:"dev"` diff --git a/config/default.go b/config/default.go index 272df1e6..d412a711 100644 --- a/config/default.go +++ b/config/default.go @@ -14,6 +14,9 @@ func NewConfig() *Config { }, SystemDockerArgs: []string{"docker", "-d", "-s", "overlay", "-b", "none"}, Modules: []string{}, + Userdocker: UserDockerInfo{ + UseTLS: true, + }, SystemContainers: []ContainerConfig{ { Cmd: "--name=system-state " + @@ -47,6 +50,8 @@ func NewConfig() *Config { "--privileged " + "-v=/lib/modules:/lib/modules:ro " + "-v=/usr/bin/docker:/usr/bin/docker:ro " + + "-v=/init:/usr/bin/tlsconf:ro " + + "-v=/init:/usr/bin/rancherctl:ro " + "--volumes-from=system-state " + "userdocker", }, diff --git a/main.go b/main.go index 2a117a75..bf2bac59 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ func main() { registerCmd("/sbin/halt", power.Halt) registerCmd("/usr/bin/respawn", respawn.Main) registerCmd("/usr/sbin/rancherctl", control.Main) - registerCmd("/sbin/tlsconf", util.TLSConf) + registerCmd("/usr/bin/tlsconf", util.TLSConf) if !reexec.Init() { log.Fatalf("Failed to find an entry point for %s", os.Args[0]) diff --git a/scripts/dockerimages/scripts/docker.sh b/scripts/dockerimages/scripts/docker.sh index 1ca3562e..21a149fa 100755 --- a/scripts/dockerimages/scripts/docker.sh +++ b/scripts/dockerimages/scripts/docker.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -x -e CGROUPS="perf_event net_cls freezer devices blkio memory cpuacct cpu cpuset" @@ -16,4 +16,33 @@ if ! lsmod | grep -q br_netfilter; then fi rm -f /var/run/docker.pid -exec docker -d -s overlay + +USE_TLS=$(rancherctl config get userdocker.use_tls) + +if [ "$USE_TLS" == "true" ]; then + TLS_CA_CERT=$(rancherctl config get userdocker.tls_ca_cert) + TLS_SERVER_CERT=$(rancherctl config get userdocker.tls_server_cert) + TLS_SERVER_KEY=$(rancherctl config get userdocker.tls_server_key) + + TLS_PATH=/etc/docker/tls + mkdir -p $TLS_PATH + + if [ -n "$TLS_CA_CERT" ] && [ -n "$TLS_SERVER_CERT" ] && [ -n "$TLS_SERVER_KEY" ]; then + echo "$TLS_CA_CERT" > $TLS_PATH/ca.pem + echo "$TLS_SERVER_CERT" > $TLS_PATH/server-cert.pem + echo "$TLS_SERVER_KEY" > $TLS_PATH/server-key.pem + else + tlsconf + TLS_CA_CERT="$(cat $TLS_PATH/ca.pem)" + TLS_SERVER_CERT="$(cat $TLS_PATH/server-cert.pem)" + TLS_SERVER_KEY="$(cat $TLS_PATH/server-key.pem)" + fi + + rancherctl config set -- userdocker.tls_ca_cert "$TLS_CA_CERT" + rancherctl config set -- userdocker.tls_server_cert "$TLS_SERVER_CERT" + rancherctl config set -- userdocker.tls_server_key "$TLS_SERVER_KEY" + + exec docker -d -s overlay --tlsverify --tlscacert=$TLS_PATH/ca.pem --tlscert=$TLS_PATH/server-cert.pem --tlskey=$TLS_PATH/server-key.pem -H=0.0.0.0:2376 -H=unix:///var/run/docker.sock +else + exec docker -d -s overlay +fi diff --git a/util/util.go b/util/util.go index a595c73c..6d91e5cf 100644 --- a/util/util.go +++ b/util/util.go @@ -26,7 +26,7 @@ func TLSConf() { caCertPath := "ca.pem" caKeyPath := "ca-key.pem" - outDir := "/var/run/" + outDir := "/etc/docker/tls/" generateCaCerts := true inputCaKey := "" From fb237dbebe04b4ff1f845ad6e90a562924aa6112 Mon Sep 17 00:00:00 2001 From: sidharthamani Date: Wed, 18 Feb 2015 19:05:23 -0800 Subject: [PATCH 2/2] add cloud-init support --- cmd/control/cloud-config.go | 406 ++++++++++++++++++ config/config.go | 1 + config/default.go | 9 + main.go | 1 + scripts/dockerimages/04-cloudconfig | 3 + .../{04-userdocker => 05-userdocker} | 0 .../dockerimages/{05-console => 06-console} | 0 scripts/dockerimages/{06-rescue => 07-rescue} | 0 scripts/dockerimages/{07-ntp => 08-ntp} | 0 scripts/dockerimages/scripts/cloud-config.sh | 7 + scripts/dockerimages/scripts/console.sh | 6 + 11 files changed, 433 insertions(+) create mode 100644 cmd/control/cloud-config.go create mode 100644 scripts/dockerimages/04-cloudconfig rename scripts/dockerimages/{04-userdocker => 05-userdocker} (100%) rename scripts/dockerimages/{05-console => 06-console} (100%) rename scripts/dockerimages/{06-rescue => 07-rescue} (100%) rename scripts/dockerimages/{07-ntp => 08-ntp} (100%) create mode 100755 scripts/dockerimages/scripts/cloud-config.sh diff --git a/cmd/control/cloud-config.go b/cmd/control/cloud-config.go new file mode 100644 index 00000000..656fe311 --- /dev/null +++ b/cmd/control/cloud-config.go @@ -0,0 +1,406 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package control + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "sync" + "time" + + "github.com/coreos/coreos-cloudinit/config" + "github.com/coreos/coreos-cloudinit/config/validate" + "github.com/coreos/coreos-cloudinit/datasource" + "github.com/coreos/coreos-cloudinit/datasource/configdrive" + "github.com/coreos/coreos-cloudinit/datasource/file" + "github.com/coreos/coreos-cloudinit/datasource/metadata/cloudsigma" + "github.com/coreos/coreos-cloudinit/datasource/metadata/digitalocean" + "github.com/coreos/coreos-cloudinit/datasource/metadata/ec2" + "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" + "github.com/coreos/coreos-cloudinit/datasource/url" + "github.com/coreos/coreos-cloudinit/datasource/waagent" + "github.com/coreos/coreos-cloudinit/initialize" + "github.com/coreos/coreos-cloudinit/network" + "github.com/coreos/coreos-cloudinit/pkg" + "github.com/coreos/coreos-cloudinit/system" + "gopkg.in/yaml.v2" +) + +const ( + version = "1.3.2+git" + datasourceInterval = 100 * time.Millisecond + datasourceMaxInterval = 30 * time.Second + datasourceTimeout = 5 * time.Minute +) + +var ( + flags = struct { + printVersion bool + ignoreFailure bool + sources struct { + file string + configDrive string + waagent string + metadataService bool + ec2MetadataService string + cloudSigmaMetadataService bool + digitalOceanMetadataService string + url string + procCmdLine bool + } + convertNetconf string + workspace string + sshKeyName string + oem string + validate bool + preInit bool + outDir string + }{} +) + +func init() { + flag.BoolVar(&flags.printVersion, "version", false, "Print the version and exit") + flag.BoolVar(&flags.ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data") + flag.StringVar(&flags.sources.file, "from-file", "", "Read user-data from provided file") + flag.StringVar(&flags.sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory") + flag.StringVar(&flags.sources.waagent, "from-waagent", "", "Read data from provided waagent directory") + flag.BoolVar(&flags.sources.metadataService, "from-metadata-service", false, "[DEPRECATED - Use -from-ec2-metadata] Download data from metadata service") + flag.StringVar(&flags.sources.ec2MetadataService, "from-ec2-metadata", "", "Download EC2 data from the provided url") + flag.BoolVar(&flags.sources.cloudSigmaMetadataService, "from-cloudsigma-metadata", false, "Download data from CloudSigma server context") + flag.StringVar(&flags.sources.digitalOceanMetadataService, "from-digitalocean-metadata", "", "Download DigitalOcean data from the provided url") + flag.StringVar(&flags.sources.url, "from-url", "", "Download user-data from provided url") + flag.BoolVar(&flags.sources.procCmdLine, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=', using the cloud-config served by an HTTP GET to ", proc_cmdline.ProcCmdlineLocation, proc_cmdline.ProcCmdlineCloudConfigFlag)) + flag.StringVar(&flags.oem, "oem", "", "Use the settings specific to the provided OEM") + flag.StringVar(&flags.convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files") + flag.StringVar(&flags.workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data") + flag.StringVar(&flags.sshKeyName, "ssh-key-name", initialize.DefaultSSHKeyName, "Add SSH keys to the system with the given name") + flag.BoolVar(&flags.validate, "validate", false, "[EXPERIMENTAL] Validate the user-data but do not apply it to the system") + flag.BoolVar(&flags.preInit, "preinit", true, "Does not initialize the cloud config. Just reads, validates and saves the merged file to disk") + flag.StringVar(&flags.outDir, "outdir", "/var/lib/rancher/", "output directory to store cloud-config") +} + +type oemConfig map[string]string + +var ( + oemConfigs = map[string]oemConfig{ + "digitalocean": oemConfig{ + "from-digitalocean-metadata": "http://169.254.169.254/", + "convert-netconf": "digitalocean", + }, + "ec2-compat": oemConfig{ + "from-ec2-metadata": "http://169.254.169.254/", + "from-configdrive": "/media/configdrive", + }, + "rackspace-onmetal": oemConfig{ + "from-configdrive": "/media/configdrive", + "convert-netconf": "debian", + }, + "azure": oemConfig{ + "from-waagent": "/var/lib/waagent", + }, + "cloudsigma": oemConfig{ + "from-cloudsigma-metadata": "true", + }, + } +) + +func CloudInit() { + failure := false + + flag.Parse() + + if !flags.preInit && flags.sources.file == "" { + fmt.Println("specify either --preinit or provide a filename to load config from") + os.Exit(2) + } + + if c, ok := oemConfigs[flags.oem]; ok { + for k, v := range c { + flag.Set(k, v) + } + } else if flags.oem != "" { + oems := make([]string, 0, len(oemConfigs)) + for k := range oemConfigs { + oems = append(oems, k) + } + fmt.Printf("Invalid option to --oem: %q. Supported options: %q\n", flags.oem, oems) + os.Exit(2) + } + + if flags.printVersion == true { + fmt.Printf("coreos-cloudinit version %s\n", version) + os.Exit(0) + } + + switch flags.convertNetconf { + case "": + case "debian": + case "digitalocean": + default: + fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, digitalocean'\n", flags.convertNetconf) + os.Exit(2) + } + + dss := getDatasources() + if len(dss) == 0 { + fmt.Println("Provide at least one of --from-file, --from-configdrive, --from-ec2-metadata, --from-cloudsigma-metadata, --from-url or --from-proc-cmdline") + os.Exit(2) + } + + ds := selectDatasource(dss) + if ds == nil { + fmt.Println("No datasources available in time") + os.Exit(1) + } + + fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type()) + userdataBytes, err := ds.FetchUserdata() + if err != nil { + fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err) + failure = true + } + + if report, err := validate.Validate(userdataBytes); err == nil { + ret := 0 + for _, e := range report.Entries() { + fmt.Println(e) + ret = 1 + } + if flags.validate { + os.Exit(ret) + } + } else { + fmt.Printf("Failed while validating user_data (%q)\n", err) + if flags.validate { + os.Exit(1) + } + } + + fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type()) + metadata, err := ds.FetchMetadata() + if err != nil { + fmt.Printf("Failed fetching meta-data from datasource: %v\n", err) + os.Exit(1) + } + + // Apply environment to user-data + env := initialize.NewEnvironment("/", ds.ConfigRoot(), flags.workspace, flags.sshKeyName, metadata) + userdata := env.Apply(string(userdataBytes)) + + var ccu *config.CloudConfig + var script *config.Script + if ud, err := initialize.ParseUserData(userdata); err != nil { + fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err) + failure = true + } else { + switch t := ud.(type) { + case *config.CloudConfig: + ccu = t + case *config.Script: + script = t + } + } + + fmt.Println("Merging cloud-config from meta-data and user-data") + cc := mergeConfigs(ccu, metadata) + + if flags.preInit { + var fileData []byte + + if script != nil { + fileData = userdataBytes + } else { + if data, err := yaml.Marshal(cc); err != nil { + fmt.Println("Error while marshalling cloud config ", err.Error()) + os.Exit(1) + } else { + fileData = data + } + } + + outDir := flags.outDir + + if len(outDir) > 0 && outDir[len(outDir)-1:] != "/" { + outDir = outDir + "/" + } + + outFile := outDir + "cloud-config" + + if err := ioutil.WriteFile(outFile, fileData, 444); err != nil { + fmt.Println("Error while writing file ", err.Error()) + os.Exit(1) + } + + os.Exit(0) + } + + var ifaces []network.InterfaceGenerator + if flags.convertNetconf != "" { + var err error + switch flags.convertNetconf { + case "debian": + ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig) + case "digitalocean": + ifaces, err = network.ProcessDigitalOceanNetconf(metadata.NetworkConfig) + default: + err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf) + } + if err != nil { + fmt.Printf("Failed to generate interfaces: %v\n", err) + os.Exit(1) + } + } + + if err = initialize.Apply(cc, ifaces, env); err != nil { + fmt.Printf("Failed to apply cloud-config: %v\n", err) + os.Exit(1) + } + + if script != nil { + if err = runScript(*script, env); err != nil { + fmt.Printf("Failed to run script: %v\n", err) + os.Exit(1) + } + } + + if failure && !flags.ignoreFailure { + os.Exit(1) + } +} + +// mergeConfigs merges certain options from md (meta-data from the datasource) +// onto cc (a CloudConfig derived from user-data), if they are not already set +// on cc (i.e. user-data always takes precedence) +func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.CloudConfig) { + if cc != nil { + out = *cc + } + + if md.Hostname != "" { + if out.Hostname != "" { + fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname) + } else { + out.Hostname = md.Hostname + } + } + for _, key := range md.SSHPublicKeys { + out.SSHAuthorizedKeys = append(out.SSHAuthorizedKeys, key) + } + return +} + +// getDatasources creates a slice of possible Datasources for cloudinit based +// on the different source command-line flags. +func getDatasources() []datasource.Datasource { + dss := make([]datasource.Datasource, 0, 5) + if flags.sources.file != "" { + dss = append(dss, file.NewDatasource(flags.sources.file)) + } + if flags.sources.url != "" { + dss = append(dss, url.NewDatasource(flags.sources.url)) + } + if flags.sources.configDrive != "" { + dss = append(dss, configdrive.NewDatasource(flags.sources.configDrive)) + } + if flags.sources.metadataService { + dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) + } + if flags.sources.ec2MetadataService != "" { + dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService)) + } + if flags.sources.cloudSigmaMetadataService { + dss = append(dss, cloudsigma.NewServerContextService()) + } + if flags.sources.digitalOceanMetadataService != "" { + dss = append(dss, digitalocean.NewDatasource(flags.sources.digitalOceanMetadataService)) + } + if flags.sources.waagent != "" { + dss = append(dss, waagent.NewDatasource(flags.sources.waagent)) + } + if flags.sources.procCmdLine { + dss = append(dss, proc_cmdline.NewDatasource()) + } + return dss +} + +// selectDatasource attempts to choose a valid Datasource to use based on its +// current availability. The first Datasource to report to be available is +// returned. Datasources will be retried if possible if they are not +// immediately available. If all Datasources are permanently unavailable or +// datasourceTimeout is reached before one becomes available, nil is returned. +func selectDatasource(sources []datasource.Datasource) datasource.Datasource { + ds := make(chan datasource.Datasource) + stop := make(chan struct{}) + var wg sync.WaitGroup + + for _, s := range sources { + wg.Add(1) + go func(s datasource.Datasource) { + defer wg.Done() + + duration := datasourceInterval + for { + fmt.Printf("Checking availability of %q\n", s.Type()) + if s.IsAvailable() { + ds <- s + return + } else if !s.AvailabilityChanges() { + return + } + select { + case <-stop: + return + case <-time.After(duration): + duration = pkg.ExpBackoff(duration, datasourceMaxInterval) + } + } + }(s) + } + + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + var s datasource.Datasource + select { + case s = <-ds: + case <-done: + case <-time.After(datasourceTimeout): + } + + close(stop) + return s +} + +// TODO(jonboulle): this should probably be refactored and moved into a different module +func runScript(script config.Script, env *initialize.Environment) error { + err := initialize.PrepWorkspace(env.Workspace()) + if err != nil { + fmt.Printf("Failed preparing workspace: %v\n", err) + return err + } + path, err := initialize.PersistScriptInWorkspace(script, env.Workspace()) + if err == nil { + var name string + name, err = system.ExecuteScript(path) + initialize.PersistUnitNameInWorkspace(name, env.Workspace()) + } + return err +} + diff --git a/config/config.go b/config/config.go index 68a513bd..221463f4 100644 --- a/config/config.go +++ b/config/config.go @@ -49,6 +49,7 @@ type Config struct { RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` State ConfigState `yaml:"state,omitempty"` Userdocker UserDockerInfo `yaml:"userdocker,omitempty"` + CloudConfig []string `yaml:"cloud_config,omitempty"` SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"` Modules []string `yaml:"modules,omitempty"` diff --git a/config/default.go b/config/default.go index d412a711..ac08d38c 100644 --- a/config/default.go +++ b/config/default.go @@ -17,6 +17,7 @@ func NewConfig() *Config { Userdocker: UserDockerInfo{ UseTLS: true, }, + CloudConfig: []string{}, SystemContainers: []ContainerConfig{ { Cmd: "--name=system-state " + @@ -34,6 +35,13 @@ func NewConfig() *Config { "-v=/lib/modules:/lib/modules:ro " + "udev", }, + { + Cmd: "--name=cloudconfig " + + "--net=host " + + "-v=/init:/usr/bin/rancherctl:ro " + + "-v=/init:/usr/bin/cloudinit:ro " + + "cloudconfig", + }, { Cmd: "--name=network " + "--cap-add=NET_ADMIN " + @@ -65,6 +73,7 @@ func NewConfig() *Config { "-v=/init:/usr/bin/system-docker:ro " + "-v=/init:/usr/bin/respawn:ro " + "-v=/var/run/docker.sock:/var/run/system-docker.sock:ro " + + "-v=/init:/usr/bin/cloudinit:ro " + "-v=/init:/sbin/poweroff:ro " + "-v=/init:/sbin/reboot:ro " + "-v=/init:/sbin/halt:ro " + diff --git a/main.go b/main.go index bf2bac59..aec88146 100644 --- a/main.go +++ b/main.go @@ -43,6 +43,7 @@ func main() { registerCmd("/usr/bin/respawn", respawn.Main) registerCmd("/usr/sbin/rancherctl", control.Main) registerCmd("/usr/bin/tlsconf", util.TLSConf) + registerCmd("/usr/bin/cloudinit", control.CloudInit) if !reexec.Init() { log.Fatalf("Failed to find an entry point for %s", os.Args[0]) diff --git a/scripts/dockerimages/04-cloudconfig b/scripts/dockerimages/04-cloudconfig new file mode 100644 index 00000000..ba555db1 --- /dev/null +++ b/scripts/dockerimages/04-cloudconfig @@ -0,0 +1,3 @@ +FROM base +COPY scripts/dockerimages/scripts/cloud-config.sh / +CMD ["/cloud-config.sh"] diff --git a/scripts/dockerimages/04-userdocker b/scripts/dockerimages/05-userdocker similarity index 100% rename from scripts/dockerimages/04-userdocker rename to scripts/dockerimages/05-userdocker diff --git a/scripts/dockerimages/05-console b/scripts/dockerimages/06-console similarity index 100% rename from scripts/dockerimages/05-console rename to scripts/dockerimages/06-console diff --git a/scripts/dockerimages/06-rescue b/scripts/dockerimages/07-rescue similarity index 100% rename from scripts/dockerimages/06-rescue rename to scripts/dockerimages/07-rescue diff --git a/scripts/dockerimages/07-ntp b/scripts/dockerimages/08-ntp similarity index 100% rename from scripts/dockerimages/07-ntp rename to scripts/dockerimages/08-ntp diff --git a/scripts/dockerimages/scripts/cloud-config.sh b/scripts/dockerimages/scripts/cloud-config.sh new file mode 100755 index 00000000..021c74a9 --- /dev/null +++ b/scripts/dockerimages/scripts/cloud-config.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -x -e + +CLOUD_CONFIG_FLAGS=$(rancherctl config get cloud_config) + +cloudinit --preinit "$CLOUD_CONFIG_FLAGS" diff --git a/scripts/dockerimages/scripts/console.sh b/scripts/dockerimages/scripts/console.sh index eb14e78a..8ac0366e 100755 --- a/scripts/dockerimages/scripts/console.sh +++ b/scripts/dockerimages/scripts/console.sh @@ -3,6 +3,12 @@ #if [ -t 1 ]; then #exec /bin/sh #else + +CLOUD_CONFIG_FILE=/var/lib/rancher/cloud-config + +if [ -s $CLOUD_CONFIG_FILE ]; then + cloudinit --from-file $CLOUD_CONFIG_FILE +fi exec respawn << EOF /sbin/getty 115200 tty1 /sbin/getty 115200 tty2