Merge pull request #40 from bcwaldon/unit-command
Implement unit.command
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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"), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user