From 4f177ee6053346ec4c360505918d366f5ccfcdc9 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 23 Feb 2017 01:16:58 +0000 Subject: [PATCH] remove systemd things that so we can build ros Signed-off-by: Sven Dowideit --- config/cloudinit/coreos-cloudinit.go | 433 ------------------------ config/cloudinit/initialize/config.go | 294 ---------------- config/cloudinit/system/networkd.go | 95 ------ config/cloudinit/system/systemd.go | 205 ----------- config/cloudinit/system/systemd_test.go | 280 --------------- 5 files changed, 1307 deletions(-) delete mode 100644 config/cloudinit/coreos-cloudinit.go delete mode 100644 config/cloudinit/initialize/config.go delete mode 100644 config/cloudinit/system/networkd.go delete mode 100644 config/cloudinit/system/systemd.go delete mode 100644 config/cloudinit/system/systemd_test.go diff --git a/config/cloudinit/coreos-cloudinit.go b/config/cloudinit/coreos-cloudinit.go deleted file mode 100644 index f7ec21c9..00000000 --- a/config/cloudinit/coreos-cloudinit.go +++ /dev/null @@ -1,433 +0,0 @@ -// 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 main - -import ( - "bytes" - "compress/gzip" - "flag" - "fmt" - "io/ioutil" - "log" - "os" - "runtime" - "sync" - "time" - - "github.com/rancher/os/config/cloudinit/config" - "github.com/rancher/os/config/cloudinit/config/validate" - "github.com/rancher/os/config/cloudinit/datasource" - "github.com/rancher/os/config/cloudinit/datasource/configdrive" - "github.com/rancher/os/config/cloudinit/datasource/file" - "github.com/rancher/os/config/cloudinit/datasource/metadata/cloudsigma" - "github.com/rancher/os/config/cloudinit/datasource/metadata/digitalocean" - "github.com/rancher/os/config/cloudinit/datasource/metadata/ec2" - "github.com/rancher/os/config/cloudinit/datasource/metadata/gce" - "github.com/rancher/os/config/cloudinit/datasource/metadata/packet" - "github.com/rancher/os/config/cloudinit/datasource/proc_cmdline" - "github.com/rancher/os/config/cloudinit/datasource/url" - "github.com/rancher/os/config/cloudinit/datasource/vmware" - "github.com/rancher/os/config/cloudinit/datasource/waagent" - "github.com/rancher/os/config/cloudinit/initialize" - "github.com/rancher/os/config/cloudinit/network" - "github.com/rancher/os/config/cloudinit/pkg" - "github.com/rancher/os/config/cloudinit/system" -) - -const ( - 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 - gceMetadataService string - cloudSigmaMetadataService bool - digitalOceanMetadataService string - packetMetadataService string - url string - procCmdLine bool - vmware bool - ovfEnv string - } - convertNetconf string - workspace string - sshKeyName string - oem string - validate bool - }{} - version = "was not built properly" -) - -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.StringVar(&flags.sources.gceMetadataService, "from-gce-metadata", "", "Download GCE 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.packetMetadataService, "from-packet-metadata", "", "Download Packet data from metadata service") - 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.BoolVar(&flags.sources.vmware, "from-vmware-guestinfo", false, "Read data from VMware guestinfo") - flag.StringVar(&flags.sources.ovfEnv, "from-vmware-ovf-env", "", "Read data from OVF Environment") - 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") -} - -type oemConfig map[string]string - -var ( - oemConfigs = map[string]oemConfig{ - "digitalocean": { - "from-digitalocean-metadata": "http://169.254.169.254/", - }, - "ec2-compat": { - "from-ec2-metadata": "http://169.254.169.254/", - "from-configdrive": "/media/configdrive", - }, - "gce": { - "from-gce-metadata": "http://metadata.google.internal/", - }, - "rackspace-onmetal": { - "from-configdrive": "/media/configdrive", - "convert-netconf": "debian", - }, - "azure": { - "from-waagent": "/var/lib/waagent", - }, - "cloudsigma": { - "from-cloudsigma-metadata": "true", - }, - "packet": { - "from-packet-metadata": "https://metadata.packet.net/", - }, - "vmware": { - "from-vmware-guestinfo": "true", - "convert-netconf": "vmware", - }, - } -) - -func main() { - failure := false - - // Conservative Go 1.5 upgrade strategy: - // keep GOMAXPROCS' default at 1 for now. - if os.Getenv("GOMAXPROCS") == "" { - runtime.GOMAXPROCS(1) - } - - flag.Parse() - - 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 %s\n", version) - os.Exit(0) - } - - switch flags.convertNetconf { - case "": - case "debian": - case "packet": - case "vmware": - default: - fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian, packet, vmware'\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-gce-metadata, --from-cloudsigma-metadata, --from-packet-metadata, --from-digitalocean-metadata, --from-vmware-guestinfo, --from-waagent, --from-url or --from-proc-cmdline") - os.Exit(2) - } - - ds := selectDatasource(dss) - if ds == nil { - log.Println("No datasources available in time") - os.Exit(1) - } - - log.Printf("Fetching user-data from datasource of type %q\n", ds.Type()) - userdataBytes, err := ds.FetchUserdata() - if err != nil { - log.Printf("Failed fetching user-data from datasource: %v. Continuing...\n", err) - failure = true - } - userdataBytes, err = decompressIfGzip(userdataBytes) - if err != nil { - log.Printf("Failed decompressing user-data from datasource: %v. Continuing...\n", err) - failure = true - } - - if report, err := validate.Validate(userdataBytes); err == nil { - ret := 0 - for _, e := range report.Entries() { - log.Println(e) - ret = 1 - } - if flags.validate { - os.Exit(ret) - } - } else { - log.Printf("Failed while validating user_data (%q)\n", err) - if flags.validate { - os.Exit(1) - } - } - - log.Printf("Fetching meta-data from datasource of type %q\n", ds.Type()) - metadata, err := ds.FetchMetadata() - if err != nil { - log.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 - switch ud, err := initialize.ParseUserData(userdata); err { - case initialize.ErrIgnitionConfig: - fmt.Printf("Detected an Ignition config. Exiting...") - os.Exit(0) - case nil: - switch t := ud.(type) { - case *config.CloudConfig: - ccu = t - case *config.Script: - script = t - } - default: - fmt.Printf("Failed to parse user-data: %v\nContinuing...\n", err) - failure = true - } - - log.Println("Merging cloud-config from meta-data and user-data") - cc := mergeConfigs(ccu, metadata) - - var ifaces []network.InterfaceGenerator - if flags.convertNetconf != "" { - var err error - switch flags.convertNetconf { - case "debian": - ifaces, err = network.ProcessDebianNetconf(metadata.NetworkConfig.([]byte)) - case "packet": - ifaces, err = network.ProcessPacketNetconf(metadata.NetworkConfig.(packet.NetworkData)) - case "vmware": - ifaces, err = network.ProcessVMwareNetconf(metadata.NetworkConfig.(map[string]string)) - default: - err = fmt.Errorf("Unsupported network config format %q", flags.convertNetconf) - } - if err != nil { - log.Printf("Failed to generate interfaces: %v\n", err) - os.Exit(1) - } - } - - if err = initialize.Apply(cc, ifaces, env); err != nil { - log.Printf("Failed to apply cloud-config: %v\n", err) - os.Exit(1) - } - - if script != nil { - if err = runScript(*script, env); err != nil { - log.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 != "" { - log.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.gceMetadataService != "" { - dss = append(dss, gce.NewDatasource(flags.sources.gceMetadataService)) - } - 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.packetMetadataService != "" { - dss = append(dss, packet.NewDatasource(flags.sources.packetMetadataService)) - } - if flags.sources.procCmdLine { - dss = append(dss, proc_cmdline.NewDatasource()) - } - if flags.sources.vmware { - dss = append(dss, vmware.NewDatasource("")) - } - if flags.sources.ovfEnv != "" { - dss = append(dss, vmware.NewDatasource(flags.sources.ovfEnv)) - } - 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 { - log.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 { - log.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 -} - -const gzipMagicBytes = "\x1f\x8b" - -func decompressIfGzip(userdataBytes []byte) ([]byte, error) { - if !bytes.HasPrefix(userdataBytes, []byte(gzipMagicBytes)) { - return userdataBytes, nil - } - gzr, err := gzip.NewReader(bytes.NewReader(userdataBytes)) - if err != nil { - return nil, err - } - defer gzr.Close() - return ioutil.ReadAll(gzr) -} diff --git a/config/cloudinit/initialize/config.go b/config/cloudinit/initialize/config.go deleted file mode 100644 index 4d2e5b20..00000000 --- a/config/cloudinit/initialize/config.go +++ /dev/null @@ -1,294 +0,0 @@ -// 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 initialize - -import ( - "errors" - "fmt" - "log" - "path" - - "github.com/rancher/os/config/cloudinit/config" - "github.com/rancher/os/config/cloudinit/network" - "github.com/rancher/os/config/cloudinit/system" -) - -// CloudConfigFile represents a CoreOS specific configuration option that can generate -// an associated system.File to be written to disk -type CloudConfigFile interface { - // File should either return (*system.File, error), or (nil, nil) if nothing - // needs to be done for this configuration option. - File() (*system.File, error) -} - -// CloudConfigUnit represents a CoreOS specific configuration option that can generate -// associated system.Units to be created/enabled appropriately -type CloudConfigUnit interface { - Units() []system.Unit -} - -// 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) -} - -func createNetworkingUnits(interfaces []network.InterfaceGenerator) (units []system.Unit) { - appendNewUnit := func(units []system.Unit, name, content string) []system.Unit { - if content == "" { - return units - } - return append(units, system.Unit{Unit: config.Unit{ - Name: name, - Runtime: true, - Content: content, - }}) - } - for _, i := range interfaces { - units = appendNewUnit(units, fmt.Sprintf("%s.netdev", i.Filename()), i.Netdev()) - units = appendNewUnit(units, fmt.Sprintf("%s.link", i.Filename()), i.Link()) - units = appendNewUnit(units, fmt.Sprintf("%s.network", i.Filename()), i.Network()) - } - return units -} - -// processUnits takes a set of Units and applies them to the given root using -// the given UnitManager. This can involve things like writing unit files to -// disk, masking/unmasking units, or invoking systemd -// commands against units. It returns any error encountered. -func processUnits(units []system.Unit, root string, um system.UnitManager) error { - type action struct { - unit system.Unit - command string - } - actions := make([]action, 0, len(units)) - reload := false - restartNetworkd := false - for _, unit := range units { - if unit.Name == "" { - log.Printf("Skipping unit without name") - continue - } - - if unit.Content != "" { - log.Printf("Writing unit %q to filesystem", unit.Name) - if err := um.PlaceUnit(unit); err != nil { - return err - } - log.Printf("Wrote unit %q", unit.Name) - reload = true - } - - for _, dropin := range unit.DropIns { - if dropin.Name != "" && dropin.Content != "" { - log.Printf("Writing drop-in unit %q to filesystem", dropin.Name) - if err := um.PlaceUnitDropIn(unit, dropin); err != nil { - return err - } - log.Printf("Wrote drop-in unit %q", dropin.Name) - reload = true - } - } - - if unit.Mask { - log.Printf("Masking unit file %q", unit.Name) - if err := um.MaskUnit(unit); err != nil { - return err - } - } else if unit.Runtime { - log.Printf("Ensuring runtime unit file %q is unmasked", unit.Name) - if err := um.UnmaskUnit(unit); err != nil { - return err - } - } - - if unit.Enable { - if unit.Group() != "network" { - log.Printf("Enabling unit file %q", unit.Name) - if err := um.EnableUnitFile(unit); err != nil { - return err - } - log.Printf("Enabled unit %q", unit.Name) - } else { - log.Printf("Skipping enable for network-like unit %q", unit.Name) - } - } - - if unit.Group() == "network" { - restartNetworkd = true - } else if unit.Command != "" { - actions = append(actions, action{unit, unit.Command}) - } - } - - if reload { - if err := um.DaemonReload(); err != nil { - return errors.New(fmt.Sprintf("failed systemd daemon-reload: %s", err)) - } - } - - if restartNetworkd { - log.Printf("Restarting systemd-networkd") - networkd := system.Unit{Unit: config.Unit{Name: "systemd-networkd.service"}} - res, err := um.RunUnitCommand(networkd, "restart") - if err != nil { - return err - } - log.Printf("Restarted systemd-networkd (%s)", res) - } - - for _, action := range actions { - log.Printf("Calling unit command %q on %q", action.command, action.unit.Name) - res, err := um.RunUnitCommand(action.unit, action.command) - if err != nil { - return err - } - log.Printf("Result of %q on %q: %s", action.command, action.unit.Name, res) - } - - return nil -} diff --git a/config/cloudinit/system/networkd.go b/config/cloudinit/system/networkd.go deleted file mode 100644 index ce6e4ad5..00000000 --- a/config/cloudinit/system/networkd.go +++ /dev/null @@ -1,95 +0,0 @@ -// 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 system - -import ( - "log" - "net" - "os/exec" - "strings" - - "github.com/rancher/os/config/cloudinit/config" - "github.com/rancher/os/config/cloudinit/network" - - "github.com/docker/docker/pkg/netlink" -) - -func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) { - defer func() { - if e := restartNetworkd(); e != nil { - err = e - } - }() - - if err = downNetworkInterfaces(interfaces); err != nil { - return - } - - if err = maybeProbe8012q(interfaces); err != nil { - return - } - return maybeProbeBonding(interfaces) -} - -func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error { - sysInterfaceMap := make(map[string]*net.Interface) - if systemInterfaces, err := net.Interfaces(); err == nil { - for _, iface := range systemInterfaces { - iface := iface - sysInterfaceMap[iface.Name] = &iface - } - } else { - return err - } - - for _, iface := range interfaces { - if systemInterface, ok := sysInterfaceMap[iface.Name()]; ok { - log.Printf("Taking down interface %q\n", systemInterface.Name) - if err := netlink.NetworkLinkDown(systemInterface); err != nil { - log.Printf("Error while downing interface %q (%s). Continuing...\n", systemInterface.Name, err) - } - } - } - - return nil -} - -func maybeProbe8012q(interfaces []network.InterfaceGenerator) error { - for _, iface := range interfaces { - if iface.Type() == "vlan" { - log.Printf("Probing LKM %q (%q)\n", "8021q", "8021q") - return exec.Command("modprobe", "8021q").Run() - } - } - return nil -} - -func maybeProbeBonding(interfaces []network.InterfaceGenerator) error { - for _, iface := range interfaces { - if iface.Type() == "bond" { - args := append([]string{"bonding"}, strings.Split(iface.ModprobeParams(), " ")...) - log.Printf("Probing LKM %q (%q)\n", "bonding", args) - return exec.Command("modprobe", args...).Run() - } - } - return nil -} - -func restartNetworkd() error { - log.Printf("Restarting networkd.service\n") - networkd := Unit{config.Unit{Name: "systemd-networkd.service"}} - _, err := NewUnitManager("").RunUnitCommand(networkd, "restart") - return err -} diff --git a/config/cloudinit/system/systemd.go b/config/cloudinit/system/systemd.go deleted file mode 100644 index 4128a3c8..00000000 --- a/config/cloudinit/system/systemd.go +++ /dev/null @@ -1,205 +0,0 @@ -// 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 system - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "os/exec" - "path" - "strings" - - "github.com/rancher/os/config/cloudinit/config" - "github.com/coreos/go-systemd/dbus" -) - -func NewUnitManager(root string) UnitManager { - return &systemd{root} -} - -type systemd struct { - root string -} - -// fakeMachineID is placed on non-usr CoreOS images and should -// never be used as a true MachineID -const fakeMachineID = "42000000000000000000000000000042" - -// PlaceUnit writes a unit file at its desired destination, creating parent -// directories as necessary. -func (s *systemd) PlaceUnit(u Unit) error { - file := File{config.File{ - Path: u.Destination(s.root), - Content: u.Content, - RawFilePermissions: "0644", - }} - - _, err := WriteFile(&file, "/") - return err -} - -// PlaceUnitDropIn writes a unit drop-in file at its desired destination, -// creating parent directories as necessary. -func (s *systemd) PlaceUnitDropIn(u Unit, d config.UnitDropIn) error { - file := File{config.File{ - Path: u.DropInDestination(s.root, d), - Content: d.Content, - RawFilePermissions: "0644", - }} - - _, err := WriteFile(&file, "/") - return err -} - -func (s *systemd) EnableUnitFile(u Unit) error { - conn, err := dbus.New() - if err != nil { - return err - } - - units := []string{u.Name} - _, _, err = conn.EnableUnitFiles(units, u.Runtime, true) - return err -} - -func (s *systemd) RunUnitCommand(u Unit, c string) (string, error) { - conn, err := dbus.New() - if err != nil { - return "", err - } - - var fn func(string, string) (string, error) - switch c { - case "start": - fn = conn.StartUnit - case "stop": - fn = conn.StopUnit - case "restart": - fn = conn.RestartUnit - case "reload": - fn = conn.ReloadUnit - case "try-restart": - fn = conn.TryRestartUnit - case "reload-or-restart": - fn = conn.ReloadOrRestartUnit - case "reload-or-try-restart": - fn = conn.ReloadOrTryRestartUnit - default: - return "", fmt.Errorf("Unsupported systemd command %q", c) - } - - return fn(u.Name, "replace") -} - -func (s *systemd) DaemonReload() error { - conn, err := dbus.New() - if err != nil { - return err - } - - return conn.Reload() -} - -// MaskUnit masks the given Unit by symlinking its unit file to -// /dev/null, analogous to `systemctl mask`. -// N.B.: Unlike `systemctl mask`, this function will *remove any existing unit -// file at the location*, to ensure that the mask will succeed. -func (s *systemd) MaskUnit(u Unit) error { - masked := u.Destination(s.root) - if _, err := os.Stat(masked); os.IsNotExist(err) { - if err := os.MkdirAll(path.Dir(masked), os.FileMode(0755)); err != nil { - return err - } - } else if err := os.Remove(masked); err != nil { - return err - } - return os.Symlink("/dev/null", masked) -} - -// UnmaskUnit is analogous to systemd's unit_file_unmask. If the file -// associated with the given Unit is empty or appears to be a symlink to -// /dev/null, it is removed. -func (s *systemd) UnmaskUnit(u Unit) error { - masked := u.Destination(s.root) - ne, err := nullOrEmpty(masked) - if os.IsNotExist(err) { - return nil - } else if err != nil { - return err - } - if !ne { - log.Printf("%s is not null or empty, refusing to unmask", masked) - return nil - } - return os.Remove(masked) -} - -// nullOrEmpty checks whether a given path appears to be an empty regular file -// or a symlink to /dev/null -func nullOrEmpty(path string) (bool, error) { - fi, err := os.Stat(path) - if err != nil { - return false, err - } - m := fi.Mode() - if m.IsRegular() && fi.Size() <= 0 { - return true, nil - } - if m&os.ModeCharDevice > 0 { - return true, nil - } - return false, nil -} - -func ExecuteScript(scriptPath string) (string, error) { - props := []dbus.Property{ - dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"), - dbus.PropExecStart([]string{"/bin/bash", scriptPath}, false), - } - - base := path.Base(scriptPath) - name := fmt.Sprintf("coreos-cloudinit-%s.service", base) - - log.Printf("Creating transient systemd unit '%s'", name) - - conn, err := dbus.New() - if err != nil { - return "", err - } - - _, err = conn.StartTransientUnit(name, "replace", props...) - return name, err -} - -func SetHostname(hostname string) error { - return exec.Command("hostnamectl", "set-hostname", hostname).Run() -} - -func Hostname() (string, error) { - return os.Hostname() -} - -func MachineID(root string) string { - contents, _ := ioutil.ReadFile(path.Join(root, "etc", "machine-id")) - id := strings.TrimSpace(string(contents)) - - if id == fakeMachineID { - id = "" - } - - return id -} diff --git a/config/cloudinit/system/systemd_test.go b/config/cloudinit/system/systemd_test.go deleted file mode 100644 index d2f02157..00000000 --- a/config/cloudinit/system/systemd_test.go +++ /dev/null @@ -1,280 +0,0 @@ -// 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 system - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "testing" - - "github.com/rancher/os/config/cloudinit/config" -) - -func TestPlaceUnit(t *testing.T) { - tests := []config.Unit{ - { - Name: "50-eth0.network", - Runtime: true, - Content: "[Match]\nName=eth47\n\n[Network]\nAddress=10.209.171.177/19\n", - }, - { - Name: "media-state.mount", - Content: "[Mount]\nWhat=/dev/sdb1\nWhere=/media/state\n", - }, - } - - for _, tt := range tests { - dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") - if err != nil { - panic(fmt.Sprintf("Unable to create tempdir: %v", err)) - } - - u := Unit{tt} - sd := &systemd{dir} - - if err := sd.PlaceUnit(u); err != nil { - t.Fatalf("PlaceUnit(): bad error (%+v): want nil, got %s", tt, err) - } - - fi, err := os.Stat(u.Destination(dir)) - if err != nil { - t.Fatalf("Stat(): bad error (%+v): want nil, got %s", tt, err) - } - - if mode := fi.Mode(); mode != os.FileMode(0644) { - t.Errorf("bad filemode (%+v): want %v, got %v", tt, os.FileMode(0644), mode) - } - - c, err := ioutil.ReadFile(u.Destination(dir)) - if err != nil { - t.Fatalf("ReadFile(): bad error (%+v): want nil, got %s", tt, err) - } - - if string(c) != tt.Content { - t.Errorf("bad contents (%+v): want %q, got %q", tt, tt.Content, string(c)) - } - - os.RemoveAll(dir) - } -} - -func TestPlaceUnitDropIn(t *testing.T) { - tests := []config.Unit{ - { - Name: "false.service", - Runtime: true, - DropIns: []config.UnitDropIn{ - { - Name: "00-true.conf", - Content: "[Service]\nExecStart=\nExecStart=/usr/bin/true\n", - }, - }, - }, - { - Name: "true.service", - DropIns: []config.UnitDropIn{ - { - Name: "00-false.conf", - Content: "[Service]\nExecStart=\nExecStart=/usr/bin/false\n", - }, - }, - }, - } - - for _, tt := range tests { - dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-") - if err != nil { - panic(fmt.Sprintf("Unable to create tempdir: %v", err)) - } - - u := Unit{tt} - sd := &systemd{dir} - - if err := sd.PlaceUnitDropIn(u, u.DropIns[0]); err != nil { - t.Fatalf("PlaceUnit(): bad error (%+v): want nil, got %s", tt, err) - } - - fi, err := os.Stat(u.DropInDestination(dir, u.DropIns[0])) - if err != nil { - t.Fatalf("Stat(): bad error (%+v): want nil, got %s", tt, err) - } - - if mode := fi.Mode(); mode != os.FileMode(0644) { - t.Errorf("bad filemode (%+v): want %v, got %v", tt, os.FileMode(0644), mode) - } - - c, err := ioutil.ReadFile(u.DropInDestination(dir, u.DropIns[0])) - if err != nil { - t.Fatalf("ReadFile(): bad error (%+v): want nil, got %s", tt, err) - } - - if string(c) != u.DropIns[0].Content { - t.Errorf("bad contents (%+v): want %q, got %q", tt, u.DropIns[0].Content, string(c)) - } - - os.RemoveAll(dir) - } -} - -func TestMachineID(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) - - os.Mkdir(path.Join(dir, "etc"), os.FileMode(0755)) - ioutil.WriteFile(path.Join(dir, "etc", "machine-id"), []byte("node007\n"), os.FileMode(0444)) - - if MachineID(dir) != "node007" { - t.Fatalf("File has incorrect contents") - } -} - -func TestMaskUnit(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) - - sd := &systemd{dir} - - // Ensure mask works with units that do not currently exist - uf := Unit{config.Unit{Name: "foo.service"}} - if err := sd.MaskUnit(uf); err != nil { - t.Fatalf("Unable to mask new unit: %v", err) - } - fooPath := path.Join(dir, "etc", "systemd", "system", "foo.service") - fooTgt, err := os.Readlink(fooPath) - if err != nil { - t.Fatal("Unable to read link", err) - } - if fooTgt != "/dev/null" { - t.Fatal("unit not masked, got unit target", fooTgt) - } - - // Ensure mask works with unit files that already exist - ub := Unit{config.Unit{Name: "bar.service"}} - barPath := path.Join(dir, "etc", "systemd", "system", "bar.service") - if _, err := os.Create(barPath); err != nil { - t.Fatalf("Error creating new unit file: %v", err) - } - if err := sd.MaskUnit(ub); err != nil { - t.Fatalf("Unable to mask existing unit: %v", err) - } - barTgt, err := os.Readlink(barPath) - if err != nil { - t.Fatal("Unable to read link", err) - } - if barTgt != "/dev/null" { - t.Fatal("unit not masked, got unit target", barTgt) - } -} - -func TestUnmaskUnit(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) - - sd := &systemd{dir} - - nilUnit := Unit{config.Unit{Name: "null.service"}} - if err := sd.UnmaskUnit(nilUnit); err != nil { - t.Errorf("unexpected error from unmasking nonexistent unit: %v", err) - } - - uf := Unit{config.Unit{Name: "foo.service", Content: "[Service]\nExecStart=/bin/true"}} - dst := uf.Destination(dir) - if err := os.MkdirAll(path.Dir(dst), os.FileMode(0755)); err != nil { - t.Fatalf("Unable to create unit directory: %v", err) - } - if _, err := os.Create(dst); err != nil { - t.Fatalf("Unable to write unit file: %v", err) - } - - if err := ioutil.WriteFile(dst, []byte(uf.Content), 700); err != nil { - t.Fatalf("Unable to write unit file: %v", err) - } - if err := sd.UnmaskUnit(uf); err != nil { - t.Errorf("unmask of non-empty unit returned unexpected error: %v", err) - } - got, _ := ioutil.ReadFile(dst) - if string(got) != uf.Content { - t.Errorf("unmask of non-empty unit mutated unit contents unexpectedly") - } - - ub := Unit{config.Unit{Name: "bar.service"}} - dst = ub.Destination(dir) - if err := os.Symlink("/dev/null", dst); err != nil { - t.Fatalf("Unable to create masked unit: %v", err) - } - if err := sd.UnmaskUnit(ub); err != nil { - t.Errorf("unmask of unit returned unexpected error: %v", err) - } - if _, err := os.Stat(dst); !os.IsNotExist(err) { - t.Errorf("expected %s to not exist after unmask, but got err: %s", dst, err) - } -} - -func TestNullOrEmpty(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) - - non := path.Join(dir, "does_not_exist") - ne, err := nullOrEmpty(non) - if !os.IsNotExist(err) { - t.Errorf("nullOrEmpty on nonexistent file returned bad error: %v", err) - } - if ne { - t.Errorf("nullOrEmpty returned true unxpectedly") - } - - regEmpty := path.Join(dir, "regular_empty_file") - _, err = os.Create(regEmpty) - if err != nil { - t.Fatalf("Unable to create tempfile: %v", err) - } - gotNe, gotErr := nullOrEmpty(regEmpty) - if !gotNe || gotErr != nil { - t.Errorf("nullOrEmpty of regular empty file returned %t, %v - want true, nil", gotNe, gotErr) - } - - reg := path.Join(dir, "regular_file") - if err := ioutil.WriteFile(reg, []byte("asdf"), 700); err != nil { - t.Fatalf("Unable to create tempfile: %v", err) - } - gotNe, gotErr = nullOrEmpty(reg) - if gotNe || gotErr != nil { - t.Errorf("nullOrEmpty of regular file returned %t, %v - want false, nil", gotNe, gotErr) - } - - null := path.Join(dir, "null") - if err := os.Symlink(os.DevNull, null); err != nil { - t.Fatalf("Unable to create /dev/null link: %s", err) - } - gotNe, gotErr = nullOrEmpty(null) - if !gotNe || gotErr != nil { - t.Errorf("nullOrEmpty of null symlink returned %t, %v - want true, nil", gotNe, gotErr) - } - -}