diff --git a/Documentation/cloud-config.md b/Documentation/cloud-config.md index 5a0b825..7fdb144 100644 --- a/Documentation/cloud-config.md +++ b/Documentation/cloud-config.md @@ -46,9 +46,16 @@ Note that hyphens in the coreos.etcd.* keys are mapped to underscores. Arbitrary systemd units may be provided in the `coreos.units` attribute. `coreos.units` is a list of objects with the following fields: -- **name**: string representing unit's name -- **runtime**: boolean indicating whether or not to persist the unit across reboots. This is analagous to the `--runtime` flag to `systemd enable`. -- **content**: plaintext string representing entire unit file +- **name**: String representing unit's name. Required. +- **runtime**: Boolean indicating whether or not to persist the unit across reboots. This is analagous to the `--runtime` argument to `systemd enable`. Default value is false. +- **content**: Plaintext string representing entire unit file. If no value is provided, the unit is assumed to exist already. +- **command**: Command to execute on unit: start, stop, reload, restart, try-restart, reload-or-restart, reload-or-try-restart. Default value is restart. + +**NOTE:** The command field is ignored for all network, netdev, and link units. The systemd-networkd.service unit will be restarted in their place. + +##### Examples + +Write a unit to disk, automatically starting it. ``` #cloud-config @@ -71,6 +78,19 @@ coreos: WantedBy=local.target ``` +Start the builtin `etcd` and `fleet` services: + +``` +# cloud-config + +coreos: + units: + - name: etcd.service + command: start + - name: fleet.service + command: start + + ## Cloud-Config Parameters ### ssh_authorized_keys diff --git a/initialize/config.go b/initialize/config.go index a902f99..a31ec51 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -14,7 +14,6 @@ type CloudConfig struct { SSHAuthorizedKeys []string `yaml:"ssh_authorized_keys"` Coreos struct { Etcd EtcdEnvironment - Fleet struct{ Autostart bool } Units []system.Unit } WriteFiles []system.File `yaml:"write_files"` @@ -115,34 +114,48 @@ func Apply(cfg CloudConfig, env *Environment) error { } if len(cfg.Coreos.Units) > 0 { + commands := make(map[string]string, 0) + for _, unit := range cfg.Coreos.Units { - log.Printf("Placing unit %s on filesystem", unit.Name) - dst, err := system.PlaceUnit(&unit, env.Root()) + if unit.Content != "" { + log.Printf("Writing unit %s to filesystem", unit.Name) + dst, err := system.PlaceUnit(&unit, env.Root()) + if err != nil { + return err + } + log.Printf("Placed unit %s at %s", unit.Name, dst) + + if unit.Group() != "network" { + log.Printf("Enabling unit file %s", dst) + if err := system.EnableUnitFile(dst, 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" { + command := unit.Command + if command == "" { + command = "restart" + } + commands[unit.Name] = command + } else { + commands["systemd-networkd.service"] = "restart" + } + } + + system.DaemonReload() + + 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("Placed unit %s at %s", unit.Name, dst) - - if unit.Group() != "network" { - log.Printf("Enabling unit file %s", dst) - if err := system.EnableUnitFile(dst, 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) - } - } - system.DaemonReload() - system.StartUnits(cfg.Coreos.Units) - } - - if cfg.Coreos.Fleet.Autostart { - err := system.StartUnitByName("fleet.service") - if err == nil { - log.Printf("Started fleet service.") - } else { - return err + log.Printf("Result of '%s %s': %s", command, unit, res) } } diff --git a/initialize/config_test.go b/initialize/config_test.go index bd5f6b8..7578a8c 100644 --- a/initialize/config_test.go +++ b/initialize/config_test.go @@ -17,10 +17,6 @@ func TestCloudConfigEmpty(t *testing.T) { t.Error("Parsed incorrect number of SSH keys") } - if cfg.Coreos.Fleet.Autostart { - t.Error("Expected AutostartFleet not to be defined") - } - if len(cfg.WriteFiles) != 0 { t.Error("Expected zero WriteFiles") } @@ -36,8 +32,6 @@ func TestCloudConfig(t *testing.T) { coreos: etcd: discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" - fleet: - autostart: Yes units: - name: 50-eth0.network runtime: yes @@ -77,10 +71,6 @@ hostname: trontastic t.Error("Expected first SSH key to be 'foobaz'") } - if !cfg.Coreos.Fleet.Autostart { - t.Error("Expected AutostartFleet to be true") - } - if len(cfg.WriteFiles) != 1 { t.Error("Failed to parse correct number of write_files") } else { diff --git a/system/systemd.go b/system/systemd.go index a2605ee..a96fb29 100644 --- a/system/systemd.go +++ b/system/systemd.go @@ -21,6 +21,7 @@ type Unit struct { Name string Runtime bool Content string + Command string } func (u *Unit) Type() string { @@ -80,34 +81,33 @@ func EnableUnitFile(file string, runtime bool) error { return err } -func separateNetworkUnits(units []Unit) ([]Unit, []Unit) { - networkUnits := make([]Unit, 0) - nonNetworkUnits := make([]Unit, 0) - for _, unit := range units { - if unit.Group() == "network" { - networkUnits = append(networkUnits, unit) - } else { - nonNetworkUnits = append(nonNetworkUnits, unit) - } - } - return networkUnits, nonNetworkUnits -} - -func StartUnits(units []Unit) error { - networkUnits, nonNetworkUnits := separateNetworkUnits(units) - if len(networkUnits) > 0 { - if err := RestartUnitByName("systemd-networkd.service"); err != nil { - return err - } +func RunUnitCommand(command, unit string) (string, error) { + conn, err := dbus.New() + if err != nil { + return "", err } - for _, unit := range nonNetworkUnits { - if err := RestartUnitByName(unit.Name); err != nil { - return err - } + var fn func(string, string) (string, error) + switch command { + 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", command) } - return nil + return fn(unit, "replace") } func DaemonReload() error { @@ -120,29 +120,6 @@ func DaemonReload() error { return err } -func RestartUnitByName(name string) error { - log.Printf("Restarting unit %s", name) - conn, err := dbus.New() - if err != nil { - return err - } - - output, err := conn.RestartUnit(name, "replace") - log.Printf("Restart completed with '%s'", output) - - return err -} - -func StartUnitByName(name string) error { - conn, err := dbus.New() - if err != nil { - return err - } - - _, err = conn.StartUnit(name, "replace") - return err -} - func ExecuteScript(scriptPath string) (string, error) { props := []dbus.Property{ dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"),