diff --git a/cmd/cloudinit/cloudinit.go b/cmd/cloudinit/cloudinit.go index c5b4b764..ea418e24 100644 --- a/cmd/cloudinit/cloudinit.go +++ b/cmd/cloudinit/cloudinit.go @@ -1,4 +1,5 @@ // 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. @@ -19,193 +20,100 @@ import ( "fmt" "io/ioutil" "os" + "strings" "sync" "time" + log "github.com/Sirupsen/logrus" "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" + rancherConfig "github.com/rancherio/os/config" "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 - }{} + outputDir string + outputFile string + save bool + sshKeyName 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") + flag.StringVar(&outputDir, "dir", "/var/lib/rancher/conf", "working directory") + flag.StringVar(&outputFile, "file", "/var/lib/rancher/conf/cloud-config.yml", "cloud config file name") + flag.StringVar(&sshKeyName, "ssh-key-name", "rancheros-cloud-config", "SSH key name") + flag.BoolVar(&save, "save", false, "save cloud config and exit") } -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 Main() { - 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) + cfg, err := rancherConfig.LoadConfig() + if err != nil { + log.Fatalf("Failed to read rancher config %v", err) } - 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() + dss := getDatasources(cfg) 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) + os.Exit(0) } ds := selectDatasource(dss) if ds == nil { - fmt.Println("No datasources available in time") - os.Exit(1) + log.Info("No datasources found") + os.Exit(0) } - fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type()) + log.Info("Fetching user-data from datasource %s", ds.Type()) userdataBytes, err := ds.FetchUserdata() if err != nil { - fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err) - failure = true + log.Fatalf("Failed fetching user-data from datasource: %v", err) } if report, err := validate.Validate(userdataBytes); err == nil { - ret := 0 + fail := false for _, e := range report.Entries() { - fmt.Println(e) - ret = 1 + log.Error(e) + fail = true } - if flags.validate { - os.Exit(ret) - } - } else { - fmt.Printf("Failed while validating user_data (%q)\n", err) - if flags.validate { + if fail { os.Exit(1) } + } else { + log.Fatalf("Failed while validating user_data (%v)", err) } - fmt.Printf("Fetching meta-data from datasource of type %q\n", ds.Type()) + fmt.Printf("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\n", err) + fmt.Printf("Failed fetching meta-data from datasource: %v", err) os.Exit(1) } // Apply environment to user-data - env := initialize.NewEnvironment("/", ds.ConfigRoot(), flags.workspace, flags.sshKeyName, metadata) + env := initialize.NewEnvironment("/", ds.ConfigRoot(), outputDir, 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 + log.Fatalf("Failed to parse user-data: %v\n", err) } else { switch t := ud.(type) { case *config.CloudConfig: @@ -218,68 +126,35 @@ func Main() { fmt.Println("Merging cloud-config from meta-data and user-data") cc := mergeConfigs(ccu, metadata) - if flags.preInit { + if save { 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) + log.Fatalf("Error while marshalling cloud config %v", err) } 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) + if err := ioutil.WriteFile(outputFile, fileData, 400); err != nil { + log.Fatalf("Error while writing file %v", err) } 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 err = initialize.Apply(cc, []network.InterfaceGenerator{}, env); err != nil { + log.Fatalf("Failed to apply cloud-config: %v", err) } if script != nil { if err = runScript(*script, env); err != nil { - fmt.Printf("Failed to run script: %v\n", err) - os.Exit(1) + log.Fatalf("Failed to run script: %v", err) } } - - if failure && !flags.ignoreFailure { - os.Exit(1) - } } // mergeConfigs merges certain options from md (meta-data from the datasource) @@ -305,35 +180,38 @@ func mergeConfigs(cc *config.CloudConfig, md datasource.Metadata) (out config.Cl // getDatasources creates a slice of possible Datasources for cloudinit based // on the different source command-line flags. -func getDatasources() []datasource.Datasource { +func getDatasources(cfg *rancherConfig.Config) []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()) + + for _, ds := range cfg.CloudInit.Datasources { + parts := strings.SplitN(ds, ":", 1) + + switch parts[0] { + case "ec2": + if len(parts) == 1 { + dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) + } else { + dss = append(dss, ec2.NewDatasource(parts[1])) + } + case "file": + if len(parts) == 2 { + dss = append(dss, file.NewDatasource(parts[1])) + } + case "url": + if len(parts) == 2 { + dss = append(dss, url.NewDatasource(parts[1])) + } + case "cmdline": + if len(parts) == 2 { + dss = append(dss, proc_cmdline.NewDatasource()) + } + case "configdrive": + if len(parts) == 2 { + dss = append(dss, configdrive.NewDatasource(parts[1])) + } + } } + return dss } diff --git a/scripts/dockerimages/04-cloudconfig b/scripts/dockerimages/04-cloudconfig deleted file mode 100644 index ba555db1..00000000 --- a/scripts/dockerimages/04-cloudconfig +++ /dev/null @@ -1,3 +0,0 @@ -FROM base -COPY scripts/dockerimages/scripts/cloud-config.sh / -CMD ["/cloud-config.sh"] diff --git a/scripts/dockerimages/04-cloudinit b/scripts/dockerimages/04-cloudinit new file mode 100644 index 00000000..b84ed95a --- /dev/null +++ b/scripts/dockerimages/04-cloudinit @@ -0,0 +1,3 @@ +FROM base +COPY scripts/dockerimages/scripts/cloud-init.sh / +CMD ["/cloud-init.sh"] diff --git a/scripts/dockerimages/scripts/cloud-config.sh b/scripts/dockerimages/scripts/cloud-init.sh similarity index 100% rename from scripts/dockerimages/scripts/cloud-config.sh rename to scripts/dockerimages/scripts/cloud-init.sh