func executeCloudConfig() error { cc, err := rancherConfig.LoadConfig() if err != nil { return err } if _, err := SetHostname(cc); err != nil { return err } if len(cc.SSHAuthorizedKeys) > 0 { authorizeSSHKeys("rancher", cc.SSHAuthorizedKeys, sshKeyName) authorizeSSHKeys("docker", cc.SSHAuthorizedKeys, sshKeyName) } for _, file := range cc.WriteFiles { f := system.File{File: file} fullPath, err := system.WriteFile(&f, "/") if err != nil { log.WithFields(log.Fields{"err": err, "path": fullPath}).Error("Error writing file") continue } log.Printf("Wrote file %s to filesystem", fullPath) } return nil }
func PersistUnitNameInWorkspace(name string, workspace string) error { file := system.File{ Path: path.Join(workspace, "scripts", "unit-name"), RawFilePermissions: "0644", Content: name, } return system.WriteFile(&file) }
func PersistUnitNameInWorkspace(name string, workspace string) error { file := system.File{File: config.File{ Path: path.Join("scripts", "unit-name"), RawFilePermissions: "0644", Content: name, }} _, err := system.WriteFile(&file, workspace) return err }
func TestOEMReleaseWrittenToDisk(t *testing.T) { oem := OEMRelease{ ID: "rackspace", Name: "Rackspace Cloud Servers", VersionID: "168.0.0", HomeURL: "https://www.rackspace.com/cloud/servers/", BugReportURL: "https://github.com/coreos/coreos-overlay", } dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") if err != nil { t.Fatalf("Unable to create tempdir: %v", err) } defer os.RemoveAll(dir) f, err := oem.File(dir) if err != nil { t.Fatalf("Processing of OEMRelease failed: %v", err) } if f == nil { t.Fatalf("OEMRelease returned nil file unexpectedly") } f.Path = path.Join(dir, f.Path) if err := system.WriteFile(f); err != nil { t.Fatalf("Writing of OEMRelease failed: %v", err) } fullPath := path.Join(dir, "etc", "oem-release") fi, err := os.Stat(fullPath) if err != nil { t.Fatalf("Unable to stat file: %v", err) } if fi.Mode() != os.FileMode(0644) { t.Errorf("File has incorrect mode: %v", fi.Mode()) } contents, err := ioutil.ReadFile(fullPath) if err != nil { t.Fatalf("Unable to read expected file: %v", err) } expect := `ID=rackspace VERSION_ID=168.0.0 NAME="Rackspace Cloud Servers" HOME_URL="https://www.rackspace.com/cloud/servers/" BUG_REPORT_URL="https://github.com/coreos/coreos-overlay" ` if string(contents) != expect { t.Fatalf("File has incorrect contents") } }
func executeCloudConfig() error { ccu, err := getSaveCloudConfig() if err != nil { return err } var metadata datasource.Metadata metaDataBytes, err := ioutil.ReadFile(rancherConfig.MetaDataFile) if err != nil { return err } if err = yaml.Unmarshal(metaDataBytes, &metadata); err != nil { return err } log.Info("Merging cloud-config from meta-data and user-data") cc := mergeConfigs(ccu, metadata) if cc.Hostname != "" { //set hostname if err := hostname.SetHostname(cc.Hostname); err != nil { log.WithFields(log.Fields{"err": err, "hostname": cc.Hostname}).Error("Error setting hostname") } } if len(cc.SSHAuthorizedKeys) > 0 { authorizeSSHKeys("rancher", cc.SSHAuthorizedKeys, sshKeyName) authorizeSSHKeys("docker", cc.SSHAuthorizedKeys, sshKeyName) } for _, user := range cc.Users { if user.Name == "" { continue } if len(user.SSHAuthorizedKeys) > 0 { authorizeSSHKeys(user.Name, user.SSHAuthorizedKeys, sshKeyName) } } for _, file := range cc.WriteFiles { f := system.File{File: file} fullPath, err := system.WriteFile(&f, "/") if err != nil { log.WithFields(log.Fields{"err": err, "path": fullPath}).Error("Error writing file") continue } log.Printf("Wrote file %s to filesystem", fullPath) } return nil }
func TestEtcHostsWrittenToDisk(t *testing.T) { dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") if err != nil { t.Fatalf("Unable to create tempdir: %v", err) } defer os.RemoveAll(dir) eh := EtcHosts("localhost") f, err := eh.File(dir) if err != nil { t.Fatalf("Error calling File on EtcHosts: %v", err) } if f == nil { t.Fatalf("manageEtcHosts returned nil file unexpectedly") } f.Path = path.Join(dir, f.Path) if err := system.WriteFile(f); err != nil { t.Fatalf("Error writing EtcHosts: %v", err) } fullPath := path.Join(dir, "etc", "hosts") fi, err := os.Stat(fullPath) if err != nil { t.Fatalf("Unable to stat file: %v", err) } if fi.Mode() != os.FileMode(0644) { t.Errorf("File has incorrect mode: %v", fi.Mode()) } contents, err := ioutil.ReadFile(fullPath) if err != nil { t.Fatalf("Unable to read expected file: %v", err) } hostname, err := os.Hostname() if err != nil { t.Fatalf("Unable to read OS hostname: %v", err) } expect := fmt.Sprintf("%s %s\n", DefaultIpv4Address, hostname) if string(contents) != expect { t.Fatalf("File has incorrect contents") } }
func TestUpdateConfWrittenToDisk(t *testing.T) { dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") if err != nil { t.Fatalf("Unable to create tempdir: %v", err) } defer os.RemoveAll(dir) setupFixtures(dir) for i := 0; i < 2; i++ { if i == 1 { err = ioutil.WriteFile(path.Join(dir, "etc", "coreos", "update.conf"), []byte(configured), 0644) if err != nil { t.Fatal(err) } } uc := &UpdateConfig{"reboot-strategy": "etcd-lock"} f, err := uc.File(dir) if err != nil { t.Fatalf("Processing UpdateConfig failed: %v", err) } else if f == nil { t.Fatal("Unexpectedly got nil updateconfig file") } f.Path = path.Join(dir, f.Path) if err := system.WriteFile(f); err != nil { t.Fatalf("Error writing update config: %v", err) } fullPath := path.Join(dir, "etc", "coreos", "update.conf") fi, err := os.Stat(fullPath) if err != nil { t.Fatalf("Unable to stat file: %v", err) } if fi.Mode() != os.FileMode(0644) { t.Errorf("File has incorrect mode: %v", fi.Mode()) } contents, err := ioutil.ReadFile(fullPath) if err != nil { t.Fatalf("Unable to read expected file: %v", err) } if string(contents) != expected { t.Fatalf("File has incorrect contents, got %v, wanted %v", string(contents), expected) } } }
func PersistScriptInWorkspace(script system.Script, workspace string) (string, error) { scriptsPath := path.Join(workspace, "scripts") tmp, err := ioutil.TempFile(scriptsPath, "") if err != nil { return "", err } tmp.Close() file := system.File{ Path: tmp.Name(), RawFilePermissions: "0744", Content: string(script), } err = system.WriteFile(&file) return file.Path, err }
func PersistScriptInWorkspace(script config.Script, workspace string) (string, error) { scriptsPath := path.Join(workspace, "scripts") tmp, err := ioutil.TempFile(scriptsPath, "") if err != nil { return "", err } tmp.Close() relpath := strings.TrimPrefix(tmp.Name(), workspace) file := system.File{File: config.File{ Path: relpath, RawFilePermissions: "0744", Content: string(script), }} return system.WriteFile(&file, workspace) }
// Apply renders a CloudConfig to an Environment. This can involve things like // configuring the hostname, adding new users, writing various configuration // files to disk, and manipulating systemd services. func Apply(cfg CloudConfig, env *Environment) error { if cfg.Hostname != "" { if err := system.SetHostname(cfg.Hostname); err != nil { return err } log.Printf("Set hostname to %s", cfg.Hostname) } for _, user := range cfg.Users { if user.Name == "" { log.Printf("User object has no 'name' field, skipping") continue } if system.UserExists(&user) { log.Printf("User '%s' exists, ignoring creation-time fields", user.Name) if user.PasswordHash != "" { log.Printf("Setting '%s' user's password", user.Name) if err := system.SetUserPassword(user.Name, user.PasswordHash); err != nil { log.Printf("Failed setting '%s' user's password: %v", user.Name, err) return err } } } else { log.Printf("Creating user '%s'", user.Name) if err := system.CreateUser(&user); err != nil { log.Printf("Failed creating user '%s': %v", user.Name, err) return err } } if len(user.SSHAuthorizedKeys) > 0 { log.Printf("Authorizing %d SSH keys for user '%s'", len(user.SSHAuthorizedKeys), user.Name) if err := system.AuthorizeSSHKeys(user.Name, env.SSHKeyName(), user.SSHAuthorizedKeys); err != nil { return err } } if user.SSHImportGithubUser != "" { log.Printf("Authorizing github user %s SSH keys for CoreOS user '%s'", user.SSHImportGithubUser, user.Name) if err := SSHImportGithubUser(user.Name, user.SSHImportGithubUser); err != nil { return err } } if user.SSHImportURL != "" { log.Printf("Authorizing SSH keys for CoreOS user '%s' from '%s'", user.Name, user.SSHImportURL) if err := SSHImportKeysFromURL(user.Name, user.SSHImportURL); err != nil { return err } } } if len(cfg.SSHAuthorizedKeys) > 0 { err := system.AuthorizeSSHKeys("core", env.SSHKeyName(), cfg.SSHAuthorizedKeys) if err == nil { log.Printf("Authorized SSH keys for core user") } else { return err } } for _, ccf := range []CloudConfigFile{cfg.Coreos.OEM, cfg.Coreos.Update, cfg.ManageEtcHosts} { f, err := ccf.File(env.Root()) if err != nil { return err } if f != nil { cfg.WriteFiles = append(cfg.WriteFiles, *f) } } for _, ccu := range []CloudConfigUnit{cfg.Coreos.Etcd, cfg.Coreos.Fleet, cfg.Coreos.Update} { u, err := ccu.Units(env.Root()) if err != nil { return err } cfg.Coreos.Units = append(cfg.Coreos.Units, u...) } wroteEnvironment := false for _, file := range cfg.WriteFiles { fullPath, err := system.WriteFile(&file, env.Root()) if err != nil { return err } if path.Clean(file.Path) == "/etc/environment" { wroteEnvironment = true } log.Printf("Wrote file %s to filesystem", fullPath) } if !wroteEnvironment { ef := env.DefaultEnvironmentFile() if ef != nil { err := system.WriteEnvFile(ef, env.Root()) if err != nil { return err } log.Printf("Updated /etc/environment") } } if env.NetconfType() != "" { netconfBytes, err := ioutil.ReadFile(path.Join(env.ConfigRoot(), cfg.NetworkConfigPath)) if err != nil { return err } var interfaces []network.InterfaceGenerator switch env.NetconfType() { case "debian": interfaces, err = network.ProcessDebianNetconf(string(netconfBytes)) default: return fmt.Errorf("Unsupported network config format %q", env.NetconfType()) } if err != nil { return err } if err := system.WriteNetworkdConfigs(interfaces); err != nil { return err } if err := system.RestartNetwork(interfaces); err != nil { return err } } um := system.NewUnitManager(env.Root()) return processUnits(cfg.Coreos.Units, env.Root(), um) }
// Apply renders a CloudConfig to an Environment. This can involve things like // configuring the hostname, adding new users, writing various configuration // files to disk, and manipulating systemd services. func Apply(cfg CloudConfig, env *Environment) error { if cfg.Hostname != "" { if err := system.SetHostname(cfg.Hostname); err != nil { return err } log.Printf("Set hostname to %s", cfg.Hostname) } for _, user := range cfg.Users { if user.Name == "" { log.Printf("User object has no 'name' field, skipping") continue } if system.UserExists(&user) { log.Printf("User '%s' exists, ignoring creation-time fields", user.Name) if user.PasswordHash != "" { log.Printf("Setting '%s' user's password", user.Name) if err := system.SetUserPassword(user.Name, user.PasswordHash); err != nil { log.Printf("Failed setting '%s' user's password: %v", user.Name, err) return err } } } else { log.Printf("Creating user '%s'", user.Name) if err := system.CreateUser(&user); err != nil { log.Printf("Failed creating user '%s': %v", user.Name, err) return err } } if len(user.SSHAuthorizedKeys) > 0 { log.Printf("Authorizing %d SSH keys for user '%s'", len(user.SSHAuthorizedKeys), user.Name) if err := system.AuthorizeSSHKeys(user.Name, env.SSHKeyName(), user.SSHAuthorizedKeys); err != nil { return err } } if user.SSHImportGithubUser != "" { log.Printf("Authorizing github user %s SSH keys for CoreOS user '%s'", user.SSHImportGithubUser, user.Name) if err := SSHImportGithubUser(user.Name, user.SSHImportGithubUser); err != nil { return err } } if user.SSHImportURL != "" { log.Printf("Authorizing SSH keys for CoreOS user '%s' from '%s'", user.Name, user.SSHImportURL) if err := SSHImportKeysFromURL(user.Name, user.SSHImportURL); err != nil { return err } } } if len(cfg.SSHAuthorizedKeys) > 0 { err := system.AuthorizeSSHKeys("core", env.SSHKeyName(), cfg.SSHAuthorizedKeys) if err == nil { log.Printf("Authorized SSH keys for core user") } else { return err } } for _, ccf := range []CloudConfigFile{cfg.Coreos.OEM, cfg.Coreos.Update, cfg.ManageEtcHosts} { f, err := ccf.File(env.Root()) if err != nil { return err } if f != nil { cfg.WriteFiles = append(cfg.WriteFiles, *f) } } for _, ccu := range []CloudConfigUnit{cfg.Coreos.Etcd, cfg.Coreos.Fleet, cfg.Coreos.Update} { u, err := ccu.Units(env.Root()) if err != nil { return err } cfg.Coreos.Units = append(cfg.Coreos.Units, u...) } for _, file := range cfg.WriteFiles { path, err := system.WriteFile(&file, env.Root()) if err != nil { return err } log.Printf("Wrote file %s to filesystem", path) } commands := make(map[string]string, 0) reload := false for _, unit := range cfg.Coreos.Units { dst := unit.Destination(env.Root()) if unit.Content != "" { log.Printf("Writing unit %s to filesystem at path %s", unit.Name, dst) if err := system.PlaceUnit(&unit, dst); err != nil { return err } log.Printf("Placed unit %s at %s", unit.Name, dst) reload = true } if unit.Mask { log.Printf("Masking unit file %s", unit.Name) if err := system.MaskUnit(&unit, env.Root()); err != nil { return err } } else if unit.Runtime { log.Printf("Ensuring runtime unit file %s is unmasked", unit.Name) if err := system.UnmaskUnit(&unit, env.Root()); err != nil { return err } } if unit.Enable { if unit.Group() != "network" { log.Printf("Enabling unit file %s", unit.Name) if err := system.EnableUnitFile(unit.Name, unit.Runtime); err != nil { return err } log.Printf("Enabled unit %s", unit.Name) } else { log.Printf("Skipping enable for network-like unit %s", unit.Name) } } if unit.Group() == "network" { commands["systemd-networkd.service"] = "restart" } else if unit.Command != "" { commands[unit.Name] = unit.Command } } if reload { if err := system.DaemonReload(); err != nil { return errors.New(fmt.Sprintf("failed systemd daemon-reload: %v", err)) } } for unit, command := range commands { log.Printf("Calling unit command '%s %s'", command, unit) res, err := system.RunUnitCommand(command, unit) if err != nil { return err } log.Printf("Result of '%s %s': %s", command, unit, res) } return nil }
// Apply renders a CloudConfig to an Environment. This can involve things like // configuring the hostname, adding new users, writing various configuration // files to disk, and manipulating systemd services. func Apply(cfg config.CloudConfig, ifaces []network.InterfaceGenerator, env *Environment) error { if cfg.Hostname != "" { if err := system.SetHostname(cfg.Hostname); err != nil { return err } log.Printf("Set hostname to %s", cfg.Hostname) } for _, user := range cfg.Users { if user.Name == "" { log.Printf("User object has no 'name' field, skipping") continue } if system.UserExists(&user) { log.Printf("User '%s' exists, ignoring creation-time fields", user.Name) if user.PasswordHash != "" { log.Printf("Setting '%s' user's password", user.Name) if err := system.SetUserPassword(user.Name, user.PasswordHash); err != nil { log.Printf("Failed setting '%s' user's password: %v", user.Name, err) return err } } } else { log.Printf("Creating user '%s'", user.Name) if err := system.CreateUser(&user); err != nil { log.Printf("Failed creating user '%s': %v", user.Name, err) return err } } if len(user.SSHAuthorizedKeys) > 0 { log.Printf("Authorizing %d SSH keys for user '%s'", len(user.SSHAuthorizedKeys), user.Name) if err := system.AuthorizeSSHKeys(user.Name, env.SSHKeyName(), user.SSHAuthorizedKeys); err != nil { return err } } if user.SSHImportGithubUser != "" { log.Printf("Authorizing github user %s SSH keys for CoreOS user '%s'", user.SSHImportGithubUser, user.Name) if err := SSHImportGithubUser(user.Name, user.SSHImportGithubUser); err != nil { return err } } for _, u := range user.SSHImportGithubUsers { log.Printf("Authorizing github user %s SSH keys for CoreOS user '%s'", u, user.Name) if err := SSHImportGithubUser(user.Name, u); err != nil { return err } } if user.SSHImportURL != "" { log.Printf("Authorizing SSH keys for CoreOS user '%s' from '%s'", user.Name, user.SSHImportURL) if err := SSHImportKeysFromURL(user.Name, user.SSHImportURL); err != nil { return err } } } if len(cfg.SSHAuthorizedKeys) > 0 { err := system.AuthorizeSSHKeys("core", env.SSHKeyName(), cfg.SSHAuthorizedKeys) if err == nil { log.Printf("Authorized SSH keys for core user") } else { return err } } var writeFiles []system.File for _, file := range cfg.WriteFiles { writeFiles = append(writeFiles, system.File{File: file}) } for _, ccf := range []CloudConfigFile{ system.OEM{OEM: cfg.CoreOS.OEM}, system.Update{Update: cfg.CoreOS.Update, ReadConfig: system.DefaultReadConfig}, system.EtcHosts{EtcHosts: cfg.ManageEtcHosts}, system.Flannel{Flannel: cfg.CoreOS.Flannel}, } { f, err := ccf.File() if err != nil { return err } if f != nil { writeFiles = append(writeFiles, *f) } } var units []system.Unit for _, u := range cfg.CoreOS.Units { units = append(units, system.Unit{Unit: u}) } for _, ccu := range []CloudConfigUnit{ system.Etcd{Etcd: cfg.CoreOS.Etcd}, system.Etcd2{Etcd2: cfg.CoreOS.Etcd2}, system.Fleet{Fleet: cfg.CoreOS.Fleet}, system.Locksmith{Locksmith: cfg.CoreOS.Locksmith}, system.Update{Update: cfg.CoreOS.Update, ReadConfig: system.DefaultReadConfig}, } { units = append(units, ccu.Units()...) } wroteEnvironment := false for _, file := range writeFiles { fullPath, err := system.WriteFile(&file, env.Root()) if err != nil { return err } if path.Clean(file.Path) == "/etc/environment" { wroteEnvironment = true } log.Printf("Wrote file %s to filesystem", fullPath) } if !wroteEnvironment { ef := env.DefaultEnvironmentFile() if ef != nil { err := system.WriteEnvFile(ef, env.Root()) if err != nil { return err } log.Printf("Updated /etc/environment") } } if len(ifaces) > 0 { units = append(units, createNetworkingUnits(ifaces)...) if err := system.RestartNetwork(ifaces); err != nil { return err } } um := system.NewUnitManager(env.Root()) return processUnits(units, env.Root(), um) }