diff --git a/cmd/cloudinit/cloudinit.go b/cmd/cloudinit/cloudinit.go index 14357693..deb2dea7 100644 --- a/cmd/cloudinit/cloudinit.go +++ b/cmd/cloudinit/cloudinit.go @@ -1,8 +1,22 @@ +// Copyright 2015 CoreOS, Inc. +// Copyright 2015 Rancher Labs, 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 cloudinit import ( "flag" - "fmt" "io/ioutil" "os" "os/exec" @@ -38,6 +52,7 @@ var ( outputDir string outputFile string save bool + execute bool sshKeyName string flags *flag.FlagSet ) @@ -45,13 +60,14 @@ var ( func init() { flags = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) flags.StringVar(&outputDir, "dir", "/var/lib/rancher/conf", "working directory") - flags.StringVar(&outputFile, "file", "cloud-config.yml", "cloud config file name") + flags.StringVar(&outputFile, "file", "cloud-config.yml", "output cloud config file name") flags.StringVar(&sshKeyName, "ssh-key-name", "rancheros-cloud-config", "SSH key name") flags.BoolVar(&save, "save", false, "save cloud config and exit") + flags.BoolVar(&execute, "execute", false, "execute saved cloud config") } func Main() { - flags.Parse(os.Args[1:]) + flags.Parse(rancherConfig.FilterGlobalConfig(os.Args[1:])) cfg, err := rancherConfig.LoadConfig() if err != nil { @@ -83,17 +99,17 @@ func Main() { fail = true } if fail { - fmt.Println("failed validation") + log.Info("failed validation") os.Exit(1) } } else { log.Fatalf("Failed while validating user_data (%v)", err) } - fmt.Printf("Fetching meta-data from datasource of type %v", ds.Type()) + log.Infof("Fetching meta-data from datasource of type %v", ds.Type()) metadata, err := ds.FetchMetadata() if err != nil { - fmt.Printf("Failed fetching meta-data from datasource: %v", err) + log.Infof("Failed fetching meta-data from datasource: %v", err) os.Exit(1) } @@ -114,7 +130,7 @@ func Main() { } } - fmt.Println("Merging cloud-config from meta-data and user-data") + log.Info("Merging cloud-config from meta-data and user-data") cc := mergeConfigs(ccu, metadata) if save { @@ -126,11 +142,12 @@ func Main() { if data, err := yaml.Marshal(cc); err != nil { log.Fatalf("Error while marshalling cloud config %v", err) } else { - fileData = data + fileData = append([]byte("#cloud-config\n"), data...) } } output := path.Join(outputDir, outputFile) + log.Infof("Writing merged cloud-config to %s", output) if err := ioutil.WriteFile(output, fileData, 400); err != nil { log.Fatalf("Error while writing file %v", err) } @@ -140,19 +157,29 @@ func Main() { if script != nil { if ds.Type() != "local-file" { - fmt.Println("can only execute local files") + log.Info("can only execute local files") } cmdPath := reflect.ValueOf(ds).Elem().Field(0).String() + if err := os.Chmod(cmdPath, 0500); err != nil { + log.Fatalf("Failed to set %s to executable : %v", cmdPath, err) + } cmd := exec.Command(cmdPath) - fmt.Println("running ", cmdPath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + log.Info("Running ", cmdPath) if err := cmd.Run(); err != nil { - fmt.Printf("Failed to run script: %v\n", err) + log.Infof("Failed to run script: %v\n", err) os.Exit(1) } } if &cc == nil { - log.Fatal("no config or script found") + log.Fatal("no config or script found") + } + + if len(cc.SSHAuthorizedKeys) > 0 { + authorizeSSHKeys("rancher", cc.SSHAuthorizedKeys, env.SSHKeyName()) } for _, user := range cc.Users { @@ -163,7 +190,7 @@ func Main() { authorizeSSHKeys(user.Name, user.SSHAuthorizedKeys, env.SSHKeyName()) } } - + for _, file := range cc.WriteFiles { f := system.File{File: file} fullPath, err := system.WriteFile(&f, env.Root()) @@ -185,7 +212,7 @@ func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.Cl if md.Hostname != "" { if out.Hostname != "" { - fmt.Printf("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname) + log.Infof("Warning: user-data hostname (%s) overrides metadata hostname (%s)\n", out.Hostname, md.Hostname) } else { out.Hostname = md.Hostname } @@ -201,6 +228,16 @@ func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.Cl func getDatasources(cfg *rancherConfig.Config) []datasource.Datasource { dss := make([]datasource.Datasource, 0, 5) + if execute { + cloudConfig := path.Join(outputDir, outputFile) + if _, err := os.Stat(cloudConfig); os.IsNotExist(err) { + return dss + } + + dss = append(dss, file.NewDatasource(cloudConfig)) + return dss + } + for _, ds := range cfg.CloudInit.Datasources { parts := strings.SplitN(ds, ":", 2) @@ -250,7 +287,7 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { duration := datasourceInterval for { - fmt.Printf("Checking availability of %q\n", s.Type()) + log.Infof("Checking availability of %q\n", s.Type()) if s.IsAvailable() { ds <- s return @@ -283,4 +320,3 @@ func selectDatasource(sources []datasource.Datasource) datasource.Datasource { close(stop) return s } - diff --git a/config/config.go b/config/config.go index 3981d70c..5746c8fb 100644 --- a/config/config.go +++ b/config/config.go @@ -47,7 +47,7 @@ type Config struct { RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` State ConfigState `yaml:"state,omitempty"` Userdocker UserDockerInfo `yaml:"userdocker,omitempty"` - OsUpgradeChannel string `yaml:"os_upgrade_channel,omitempty"` + OsUpgradeChannel string `yaml:"os_upgrade_channel,omitempty"` SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"` Modules []string `yaml:"modules,omitempty"` @@ -75,7 +75,7 @@ type ConfigState struct { } type CloudInit struct { - Datasources []string `yaml:"datasources"` + Datasources []string `yaml:"datasources,omitempty"` } func (c *Config) PrivilegedMerge(newConfig Config) (bool, error) { @@ -158,6 +158,17 @@ func LoadConfig() (*Config, error) { return cfg, nil } +func FilterGlobalConfig(input []string) []string { + result := make([]string, 0, len(input)) + for _, value := range input { + if !strings.HasPrefix(value, "--rancher") { + result = append(result, value) + } + } + + return result +} + func (c *Config) readArgs() error { log.Debug("Reading config args") parts := make([]string, len(os.Args)) @@ -167,8 +178,9 @@ func (c *Config) readArgs() error { arg = arg[2:] } - arg = strings.Replace(arg, "-", ".", -1) - parts = append(parts, arg) + kv := strings.SplitN(arg, "=", 2) + kv[0] = strings.Replace(kv[0], "-", ".", -1) + parts = append(parts, strings.Join(kv, "=")) } cmdLine := strings.Join(parts, " ") diff --git a/config/default.go b/config/default.go index c264089c..0c1295d5 100644 --- a/config/default.go +++ b/config/default.go @@ -17,8 +17,8 @@ func NewConfig() *Config { Userdocker: UserDockerInfo{ UseTLS: true, }, - CloudInit: CloudInit{ - Datasources: []string{"file:/home/rancher/cloudconfig"}, + CloudInit: CloudInit{ + Datasources: []string{"configdrive:/media/config-2"}, }, SystemContainers: []ContainerConfig{ { @@ -72,8 +72,10 @@ func NewConfig() *Config { Id: "cloud-init", Cmd: "--name=cloud-init " + "--rm " + + "--privileged " + "--net=host " + "--volumes-from=command-volumes " + + "--volumes-from=system-volumes " + "cloudinit", ReloadConfig: true, }, diff --git a/scripts/dockerimages/scripts/cloud-init.sh b/scripts/dockerimages/scripts/cloud-init.sh index 9b544183..28a9d684 100755 --- a/scripts/dockerimages/scripts/cloud-init.sh +++ b/scripts/dockerimages/scripts/cloud-init.sh @@ -1,5 +1,17 @@ #!/bin/bash - set -x -e -cloud-init +MOUNT_POINT=/media/config-2 +CONFIG_DEV=$(blkid | grep -- 'LABEL="config-2"' | cut -f1 -d:) + +mkdir -p ${MOUNT_POINT} + +if [ -e "${CONFIG_DEV}" ]; then + mount ${CONFIG_DEV} ${MOUNT_POINT} +else + mount -t 9p -o trans=virtio,version=9p2000.L config-2 ${MOUNT_POINT} 2>/dev/null || true +fi + +rancherctl config get cloud_init + +cloud-init -save diff --git a/scripts/dockerimages/scripts/console.sh b/scripts/dockerimages/scripts/console.sh index 100f0582..222e87ad 100755 --- a/scripts/dockerimages/scripts/console.sh +++ b/scripts/dockerimages/scripts/console.sh @@ -1,8 +1,6 @@ #!/bin/bash set -e -CLOUD_CONFIG_FILE=/var/lib/rancher/cloud-config - setup_ssh() { for i in rsa dsa ecdsa ed25519; do @@ -28,10 +26,7 @@ setup_ssh() mkdir -p /var/run/sshd } - -if [ -s $CLOUD_CONFIG_FILE ]; then - cloud-init --from-file $CLOUD_CONFIG_FILE -fi +cloud-init -execute setup_ssh diff --git a/scripts/dockerimages/scripts/update-ssh-keys b/scripts/dockerimages/scripts/update-ssh-keys index 78bc624b..f2a0c637 100755 --- a/scripts/dockerimages/scripts/update-ssh-keys +++ b/scripts/dockerimages/scripts/update-ssh-keys @@ -5,9 +5,16 @@ HOME_DIR=$(grep ^$USERNAME /etc/passwd | cut -f6 -d:) if [ ! -d $HOME_DIR/.ssh ]; then mkdir -p $HOME_DIR/.ssh + chmod 0700 $HOME_DIR/.ssh fi +if [ ! -e $HOME_DIR/.ssh/authorized_keys ]; then + touch $HOME_DIR/.ssh/authorized_keys + chmod 0600 $HOME_DIR/.ssh/authorized_keys +fi -if [ ! grep -q $HOME_DIR/.ssh/authorized_keys ]; then +if ! grep -q "$2" $HOME_DIR/.ssh/authorized_keys; then echo "$2" >> $HOME_DIR/.ssh/authorized_keys fi + +chown -R $USERNAME $HOME_DIR/.ssh diff --git a/scripts/extraimages/00-ubuntuconsole b/scripts/extraimages/00-ubuntuconsole index 4d232244..159ed92a 100644 --- a/scripts/extraimages/00-ubuntuconsole +++ b/scripts/extraimages/00-ubuntuconsole @@ -4,6 +4,7 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends openssh-server RUN rm -rf /etc/ssh/*key* COPY scripts/dockerimages/scripts/console.sh /usr/sbin/ +COPY scripts/dockerimages/scripts/update-ssh-keys /usr/bin/ RUN echo 'RancherOS \\n \l' > /etc/issue RUN locale-gen en_US.UTF-8 RUN addgroup --gid 1100 rancher && \