1
0
mirror of https://github.com/rancher/os.git synced 2025-09-03 15:54:24 +00:00

Refactor cloud-init

This commit is contained in:
Darren Shepherd
2015-02-19 20:05:17 -07:00
parent c2a8de2b5f
commit e211279636
4 changed files with 71 additions and 193 deletions

View File

@@ -1,4 +1,5 @@
// Copyright 2015 CoreOS, Inc. // Copyright 2015 CoreOS, Inc.
// Copyright 2015 Rancher Labs, Inc.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@@ -19,193 +20,100 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
log "github.com/Sirupsen/logrus"
"github.com/coreos/coreos-cloudinit/config" "github.com/coreos/coreos-cloudinit/config"
"github.com/coreos/coreos-cloudinit/config/validate" "github.com/coreos/coreos-cloudinit/config/validate"
"github.com/coreos/coreos-cloudinit/datasource" "github.com/coreos/coreos-cloudinit/datasource"
"github.com/coreos/coreos-cloudinit/datasource/configdrive" "github.com/coreos/coreos-cloudinit/datasource/configdrive"
"github.com/coreos/coreos-cloudinit/datasource/file" "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/metadata/ec2"
"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
"github.com/coreos/coreos-cloudinit/datasource/url" "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/initialize"
"github.com/coreos/coreos-cloudinit/network" "github.com/coreos/coreos-cloudinit/network"
"github.com/coreos/coreos-cloudinit/pkg" "github.com/coreos/coreos-cloudinit/pkg"
"github.com/coreos/coreos-cloudinit/system" "github.com/coreos/coreos-cloudinit/system"
rancherConfig "github.com/rancherio/os/config"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
const ( const (
version = "1.3.2+git"
datasourceInterval = 100 * time.Millisecond datasourceInterval = 100 * time.Millisecond
datasourceMaxInterval = 30 * time.Second datasourceMaxInterval = 30 * time.Second
datasourceTimeout = 5 * time.Minute datasourceTimeout = 5 * time.Minute
) )
var ( var (
flags = struct { outputDir string
printVersion bool outputFile string
ignoreFailure bool save bool
sources struct { sshKeyName string
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() { func init() {
flag.BoolVar(&flags.printVersion, "version", false, "Print the version and exit") flag.StringVar(&outputDir, "dir", "/var/lib/rancher/conf", "working directory")
flag.BoolVar(&flags.ignoreFailure, "ignore-failure", false, "Exits with 0 status in the event of malformed input from user-data") flag.StringVar(&outputFile, "file", "/var/lib/rancher/conf/cloud-config.yml", "cloud config file name")
flag.StringVar(&flags.sources.file, "from-file", "", "Read user-data from provided file") flag.StringVar(&sshKeyName, "ssh-key-name", "rancheros-cloud-config", "SSH key name")
flag.StringVar(&flags.sources.configDrive, "from-configdrive", "", "Read data from provided cloud-drive directory") flag.BoolVar(&save, "save", false, "save cloud config and exit")
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=<url>', using the cloud-config served by an HTTP GET to <url>", 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 Main() { func Main() {
failure := false
flag.Parse() flag.Parse()
if !flags.preInit && flags.sources.file == "" { cfg, err := rancherConfig.LoadConfig()
fmt.Println("specify either --preinit or provide a filename to load config from") if err != nil {
os.Exit(2) log.Fatalf("Failed to read rancher config %v", err)
} }
if c, ok := oemConfigs[flags.oem]; ok { dss := getDatasources(cfg)
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 { 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(0)
os.Exit(2)
} }
ds := selectDatasource(dss) ds := selectDatasource(dss)
if ds == nil { if ds == nil {
fmt.Println("No datasources available in time") log.Info("No datasources found")
os.Exit(1) 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() userdataBytes, err := ds.FetchUserdata()
if err != nil { if err != nil {
fmt.Printf("Failed fetching user-data from datasource: %v\nContinuing...\n", err) log.Fatalf("Failed fetching user-data from datasource: %v", err)
failure = true
} }
if report, err := validate.Validate(userdataBytes); err == nil { if report, err := validate.Validate(userdataBytes); err == nil {
ret := 0 fail := false
for _, e := range report.Entries() { for _, e := range report.Entries() {
fmt.Println(e) log.Error(e)
ret = 1 fail = true
} }
if flags.validate { if fail {
os.Exit(ret)
}
} else {
fmt.Printf("Failed while validating user_data (%q)\n", err)
if flags.validate {
os.Exit(1) 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() metadata, err := ds.FetchMetadata()
if err != nil { 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) os.Exit(1)
} }
// Apply environment to user-data // 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)) userdata := env.Apply(string(userdataBytes))
var ccu *config.CloudConfig var ccu *config.CloudConfig
var script *config.Script var script *config.Script
if ud, err := initialize.ParseUserData(userdata); err != nil { if ud, err := initialize.ParseUserData(userdata); err != nil {
fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err) log.Fatalf("Failed to parse user-data: %v\n", err)
failure = true
} else { } else {
switch t := ud.(type) { switch t := ud.(type) {
case *config.CloudConfig: case *config.CloudConfig:
@@ -218,68 +126,35 @@ func Main() {
fmt.Println("Merging cloud-config from meta-data and user-data") fmt.Println("Merging cloud-config from meta-data and user-data")
cc := mergeConfigs(ccu, metadata) cc := mergeConfigs(ccu, metadata)
if flags.preInit { if save {
var fileData []byte var fileData []byte
if script != nil { if script != nil {
fileData = userdataBytes fileData = userdataBytes
} else { } else {
if data, err := yaml.Marshal(cc); err != nil { if data, err := yaml.Marshal(cc); err != nil {
fmt.Println("Error while marshalling cloud config ", err.Error()) log.Fatalf("Error while marshalling cloud config %v", err)
os.Exit(1)
} else { } else {
fileData = data fileData = data
} }
} }
outDir := flags.outDir if err := ioutil.WriteFile(outputFile, fileData, 400); err != nil {
log.Fatalf("Error while writing file %v", err)
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) os.Exit(0)
} }
var ifaces []network.InterfaceGenerator if err = initialize.Apply(cc, []network.InterfaceGenerator{}, env); err != nil {
if flags.convertNetconf != "" { log.Fatalf("Failed to apply cloud-config: %v", err)
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 script != nil {
if err = runScript(*script, env); err != nil { if err = runScript(*script, env); err != nil {
fmt.Printf("Failed to run script: %v\n", err) log.Fatalf("Failed to run script: %v", err)
os.Exit(1)
} }
} }
if failure && !flags.ignoreFailure {
os.Exit(1)
}
} }
// mergeConfigs merges certain options from md (meta-data from the datasource) // 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 // getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags. // on the different source command-line flags.
func getDatasources() []datasource.Datasource { func getDatasources(cfg *rancherConfig.Config) []datasource.Datasource {
dss := make([]datasource.Datasource, 0, 5) dss := make([]datasource.Datasource, 0, 5)
if flags.sources.file != "" {
dss = append(dss, file.NewDatasource(flags.sources.file)) for _, ds := range cfg.CloudInit.Datasources {
} parts := strings.SplitN(ds, ":", 1)
if flags.sources.url != "" {
dss = append(dss, url.NewDatasource(flags.sources.url)) switch parts[0] {
} case "ec2":
if flags.sources.configDrive != "" { if len(parts) == 1 {
dss = append(dss, configdrive.NewDatasource(flags.sources.configDrive)) dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress))
} } else {
if flags.sources.metadataService { dss = append(dss, ec2.NewDatasource(parts[1]))
dss = append(dss, ec2.NewDatasource(ec2.DefaultAddress)) }
} case "file":
if flags.sources.ec2MetadataService != "" { if len(parts) == 2 {
dss = append(dss, ec2.NewDatasource(flags.sources.ec2MetadataService)) dss = append(dss, file.NewDatasource(parts[1]))
} }
if flags.sources.cloudSigmaMetadataService { case "url":
dss = append(dss, cloudsigma.NewServerContextService()) if len(parts) == 2 {
} dss = append(dss, url.NewDatasource(parts[1]))
if flags.sources.digitalOceanMetadataService != "" { }
dss = append(dss, digitalocean.NewDatasource(flags.sources.digitalOceanMetadataService)) case "cmdline":
} if len(parts) == 2 {
if flags.sources.waagent != "" { dss = append(dss, proc_cmdline.NewDatasource())
dss = append(dss, waagent.NewDatasource(flags.sources.waagent)) }
} case "configdrive":
if flags.sources.procCmdLine { if len(parts) == 2 {
dss = append(dss, proc_cmdline.NewDatasource()) dss = append(dss, configdrive.NewDatasource(parts[1]))
}
}
} }
return dss return dss
} }

View File

@@ -1,3 +0,0 @@
FROM base
COPY scripts/dockerimages/scripts/cloud-config.sh /
CMD ["/cloud-config.sh"]

View File

@@ -0,0 +1,3 @@
FROM base
COPY scripts/dockerimages/scripts/cloud-init.sh /
CMD ["/cloud-init.sh"]