Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
639c693153 | ||
|
b4027077ff | ||
|
580460ff3f | ||
|
b246ec0397 | ||
|
4977c774d8 | ||
|
661bae11fc | ||
|
58ae898948 | ||
|
f5f9a0a6a9 | ||
|
477ae29135 | ||
|
0203d4a9f3 | ||
|
e68134d884 | ||
|
2ad33487d7 | ||
|
b778fe6f41 | ||
|
3d7bda9f6b | ||
|
3d01211937 | ||
|
61808c2002 |
@@ -39,6 +39,45 @@ Note that hyphens in the coreos.etcd.* keys are mapped to underscores.
|
|||||||
|
|
||||||
[etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
|
[etcd-config]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
|
||||||
|
|
||||||
|
### coreos.oem
|
||||||
|
|
||||||
|
These fields are borrowed from the [os-release spec][os-release] and repurposed
|
||||||
|
as a way for coreos-cloudinit to know about the OEM partition on this machine:
|
||||||
|
|
||||||
|
- **id**: Lowercase string identifying the OEM
|
||||||
|
- **name**: Human-friendly string representing the OEM
|
||||||
|
- **version-id**: Lowercase string identifying the version of the OEM
|
||||||
|
- **home-url**: Link to the homepage of the provider or OEM
|
||||||
|
- **bug-report-url**: Link to a place to file bug reports about this OEM
|
||||||
|
|
||||||
|
coreos-cloudinit renders these fields to `/etc/oem-release`.
|
||||||
|
If no **id** field is provided, coreos-cloudinit will ignore this section.
|
||||||
|
|
||||||
|
For example, the following cloud-config document...
|
||||||
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
coreos:
|
||||||
|
oem:
|
||||||
|
id: rackspace
|
||||||
|
name: Rackspace Cloud Servers
|
||||||
|
version-id: 168.0.0
|
||||||
|
home-url: https://www.rackspace.com/cloud/servers/
|
||||||
|
bug-report-url: https://github.com/coreos/coreos-overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
...would be rendered to the following `/etc/oem-release`:
|
||||||
|
|
||||||
|
```
|
||||||
|
ID="rackspace"
|
||||||
|
NAME="Rackspace Cloud Servers"
|
||||||
|
VERSION_ID="168.0.0"
|
||||||
|
HOME_URL="https://www.rackspace.com/cloud/servers/"
|
||||||
|
BUG_REPORT_URL="https://github.com/coreos/coreos-overlay"
|
||||||
|
```
|
||||||
|
|
||||||
|
[os-release]: http://www.freedesktop.org/software/systemd/man/os-release.html
|
||||||
|
|
||||||
### coreos.units
|
### coreos.units
|
||||||
|
|
||||||
Arbitrary systemd units may be provided in the `coreos.units` attribute.
|
Arbitrary systemd units may be provided in the `coreos.units` attribute.
|
||||||
|
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/coreos/coreos-cloudinit/system"
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.2.2"
|
const version = "0.3.2"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var printVersion bool
|
var printVersion bool
|
||||||
|
@@ -15,6 +15,7 @@ type CloudConfig struct {
|
|||||||
Coreos struct {
|
Coreos struct {
|
||||||
Etcd EtcdEnvironment
|
Etcd EtcdEnvironment
|
||||||
Units []system.Unit
|
Units []system.Unit
|
||||||
|
OEM OEMRelease
|
||||||
}
|
}
|
||||||
WriteFiles []system.File `yaml:"write_files"`
|
WriteFiles []system.File `yaml:"write_files"`
|
||||||
Hostname string
|
Hostname string
|
||||||
@@ -47,6 +48,13 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
log.Printf("Set hostname to %s", cfg.Hostname)
|
log.Printf("Set hostname to %s", cfg.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Coreos.OEM.ID != "" {
|
||||||
|
if err := WriteOEMRelease(&cfg.Coreos.OEM, env.Root()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Wrote /etc/oem-release to filesystem")
|
||||||
|
}
|
||||||
|
|
||||||
if len(cfg.Users) > 0 {
|
if len(cfg.Users) > 0 {
|
||||||
for _, user := range cfg.Users {
|
for _, user := range cfg.Users {
|
||||||
if user.Name == "" {
|
if user.Name == "" {
|
||||||
@@ -136,18 +144,14 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if unit.Group() != "network" {
|
if unit.Group() == "network" {
|
||||||
command := unit.Command
|
|
||||||
if command == "" {
|
|
||||||
command = "restart"
|
|
||||||
}
|
|
||||||
commands[unit.Name] = command
|
|
||||||
} else {
|
|
||||||
commands["systemd-networkd.service"] = "restart"
|
commands["systemd-networkd.service"] = "restart"
|
||||||
|
} else {
|
||||||
|
if unit.Command != "" {
|
||||||
|
commands[unit.Name] = unit.Command
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
system.DaemonReload()
|
|
||||||
|
|
||||||
for unit, command := range commands {
|
for unit, command := range commands {
|
||||||
log.Printf("Calling unit command '%s %s'", command, unit)
|
log.Printf("Calling unit command '%s %s'", command, unit)
|
||||||
|
@@ -45,6 +45,12 @@ coreos:
|
|||||||
Address=10.209.171.177/19
|
Address=10.209.171.177/19
|
||||||
|
|
||||||
'
|
'
|
||||||
|
oem:
|
||||||
|
id: rackspace
|
||||||
|
name: Rackspace Cloud Servers
|
||||||
|
version-id: 168.0.0
|
||||||
|
home-url: https://www.rackspace.com/cloud/servers/
|
||||||
|
bug-report-url: https://github.com/coreos/coreos-overlay
|
||||||
ssh_authorized_keys:
|
ssh_authorized_keys:
|
||||||
- foobar
|
- foobar
|
||||||
- foobaz
|
- foobaz
|
||||||
@@ -116,6 +122,10 @@ Address=10.209.171.177/19
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Coreos.OEM.ID != "rackspace" {
|
||||||
|
t.Errorf("Failed parsing coreos.oem. Expected ID 'rackspace', got %q.", cfg.Coreos.OEM.ID)
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.Hostname != "trontastic" {
|
if cfg.Hostname != "trontastic" {
|
||||||
t.Errorf("Failed to parse hostname")
|
t.Errorf("Failed to parse hostname")
|
||||||
}
|
}
|
||||||
|
39
initialize/oem.go
Normal file
39
initialize/oem.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OEMRelease struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
VersionID string `yaml:"version-id"`
|
||||||
|
HomeURL string `yaml:"home-url"`
|
||||||
|
BugReportURL string `yaml:"bug-report-url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oem *OEMRelease) String() string {
|
||||||
|
fields := []string{
|
||||||
|
fmt.Sprintf("ID=%q", oem.ID),
|
||||||
|
fmt.Sprintf("NAME=%q", oem.Name),
|
||||||
|
fmt.Sprintf("VERSION_ID=%q", oem.VersionID),
|
||||||
|
fmt.Sprintf("HOME_URL=%q", oem.HomeURL),
|
||||||
|
fmt.Sprintf("BUG_REPORT_URL=%q", oem.BugReportURL),
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(fields, "\n") + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteOEMRelease(oem *OEMRelease, root string) error {
|
||||||
|
file := system.File{
|
||||||
|
Path: path.Join(root, "etc", "oem-release"),
|
||||||
|
RawFilePermissions: "0644",
|
||||||
|
Content: oem.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return system.WriteFile(&file)
|
||||||
|
}
|
54
initialize/oem_test.go
Normal file
54
initialize/oem_test.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 syscall.Rmdir(dir)
|
||||||
|
|
||||||
|
if err := WriteOEMRelease(&oem, dir); err != nil {
|
||||||
|
t.Fatalf("Processing of EtcdEnvironment 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"
|
||||||
|
NAME="Rackspace Cloud Servers"
|
||||||
|
VERSION_ID="168.0.0"
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
@@ -116,8 +116,7 @@ func DaemonReload() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.Reload()
|
return conn.Reload()
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteScript(scriptPath string) (string, error) {
|
func ExecuteScript(scriptPath string) (string, error) {
|
||||||
|
@@ -18,6 +18,8 @@ limitations under the License.
|
|||||||
package dbus
|
package dbus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -73,7 +75,12 @@ func (c *Conn) initConnection() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.sysconn.Auth(nil)
|
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||||
|
// to avoid a username lookup (which requires a dynamically linked
|
||||||
|
// libc)
|
||||||
|
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||||
|
|
||||||
|
err = c.sysconn.Auth(methods)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sysconn.Close()
|
c.sysconn.Close()
|
||||||
return err
|
return err
|
||||||
|
@@ -35,6 +35,7 @@ func (c *Conn) jobComplete(signal *dbus.Signal) {
|
|||||||
out, ok := c.jobListener.jobs[job]
|
out, ok := c.jobListener.jobs[job]
|
||||||
if ok {
|
if ok {
|
||||||
out <- result
|
out <- result
|
||||||
|
delete(c.jobListener.jobs, job)
|
||||||
}
|
}
|
||||||
c.jobListener.Unlock()
|
c.jobListener.Unlock()
|
||||||
}
|
}
|
||||||
@@ -137,8 +138,8 @@ func (c *Conn) KillUnit(name string, signal int32) {
|
|||||||
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
|
||||||
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
var props map[string]dbus.Variant
|
var props map[string]dbus.Variant
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@ func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, "org.freedesktop.systemd1.Unit").Store(&props)
|
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -161,6 +162,55 @@ func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||||
|
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
|
||||||
|
var err error
|
||||||
|
var prop dbus.Variant
|
||||||
|
|
||||||
|
path := ObjectPath("/org/freedesktop/systemd1/unit/" + unit)
|
||||||
|
if !path.IsValid() {
|
||||||
|
return nil, errors.New("invalid unit name: " + unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||||
|
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Property{Name: propertyName, Value: prop}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
|
||||||
|
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
|
||||||
|
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
|
||||||
|
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
|
||||||
|
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnitProperties() may be used to modify certain unit properties at runtime.
|
||||||
|
// Not all properties may be changed at runtime, but many resource management
|
||||||
|
// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
|
||||||
|
// instantly, and stored on disk for future boots, unless runtime is true, in which
|
||||||
|
// case the settings only apply until the next reboot. name is the name of the unit
|
||||||
|
// to modify. properties are the settings to set, encoded as an array of property
|
||||||
|
// name and value pairs.
|
||||||
|
func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
|
||||||
|
return c.sysobj.Call("SetUnitProperties", 0, name, runtime, properties).Store()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
|
||||||
|
return c.getProperty(unit, "org.freedesktop.systemd1." + unitType, propertyName)
|
||||||
|
}
|
||||||
|
|
||||||
// ListUnits returns an array with all currently loaded units. Note that
|
// ListUnits returns an array with all currently loaded units. Note that
|
||||||
// units may be known by multiple names at the same time, and hence there might
|
// units may be known by multiple names at the same time, and hence there might
|
||||||
// be more unit names loaded than actual units behind them.
|
// be more unit names loaded than actual units behind them.
|
||||||
@@ -253,8 +303,52 @@ type EnableUnitFileChange struct {
|
|||||||
Destination string // Destination of the symlink
|
Destination string // Destination of the symlink
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DisableUnitFiles() may be used to disable one or more units in the system (by
|
||||||
|
// removing symlinks to them from /etc or /run).
|
||||||
|
//
|
||||||
|
// It takes a list of unit files to disable (either just file names or full
|
||||||
|
// absolute paths if the unit files are residing outside the usual unit
|
||||||
|
// search paths), and one boolean: whether the unit was enabled for runtime
|
||||||
|
// only (true, /run), or persistently (false, /etc).
|
||||||
|
//
|
||||||
|
// This call returns an array with the changes made. The changes list
|
||||||
|
// consists of structures with three strings: the type of the change (one of
|
||||||
|
// symlink or unlink), the file name of the symlink and the destination of the
|
||||||
|
// symlink.
|
||||||
|
func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
|
||||||
|
result := make([][]interface{}, 0)
|
||||||
|
err := c.sysobj.Call("DisableUnitFiles", 0, files, runtime).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultInterface := make([]interface{}, len(result))
|
||||||
|
for i := range result {
|
||||||
|
resultInterface[i] = result[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make([]DisableUnitFileChange, len(result))
|
||||||
|
changesInterface := make([]interface{}, len(changes))
|
||||||
|
for i := range changes {
|
||||||
|
changesInterface[i] = &changes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dbus.Store(resultInterface, changesInterface...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisableUnitFileChange struct {
|
||||||
|
Type string // Type of the change (one of symlink or unlink)
|
||||||
|
Filename string // File name of the symlink
|
||||||
|
Destination string // Destination of the symlink
|
||||||
|
}
|
||||||
|
|
||||||
// Reload instructs systemd to scan for and reload unit files. This is
|
// Reload instructs systemd to scan for and reload unit files. This is
|
||||||
// equivalent to a 'systemctl daemon-reload'.
|
// equivalent to a 'systemctl daemon-reload'.
|
||||||
func (c *Conn) Reload() (string, error) {
|
func (c *Conn) Reload() error {
|
||||||
return c.runJob("org.freedesktop.systemd1.Manager.Reload")
|
return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
|
||||||
}
|
}
|
||||||
|
@@ -18,9 +18,11 @@ package dbus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/coreos/coreos-cloudinit/third_party/github.com/guelfey/go.dbus"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,13 +52,16 @@ func setupUnit(target string, conn *Conn, t *testing.T) {
|
|||||||
fixture := []string{abs}
|
fixture := []string{abs}
|
||||||
|
|
||||||
install, changes, err := conn.EnableUnitFiles(fixture, true, true)
|
install, changes, err := conn.EnableUnitFiles(fixture, true, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if install != false {
|
if install != false {
|
||||||
t.Fatal("Install was true")
|
t.Fatal("Install was true")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(changes) < 1 {
|
if len(changes) < 1 {
|
||||||
t.Fatal("Expected one change, got %v", changes)
|
t.Fatalf("Expected one change, got %v", changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if changes[0].Filename != targetRun {
|
if changes[0].Filename != targetRun {
|
||||||
@@ -118,6 +123,37 @@ func TestStartStopUnit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enables a unit and then immediately tears it down
|
||||||
|
func TestEnableDisableUnit(t *testing.T) {
|
||||||
|
target := "enable-disable.service"
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
abs, err := filepath.Abs("../fixtures/" + target)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join("/run/systemd/system/", target)
|
||||||
|
|
||||||
|
// 2. Disable the unit
|
||||||
|
changes, err := conn.DisableUnitFiles([]string{abs}, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) != 1 {
|
||||||
|
t.Fatalf("Changes should include the path, %v", changes)
|
||||||
|
}
|
||||||
|
if changes[0].Filename != path {
|
||||||
|
t.Fatalf("Change should include correct filename, %+v", changes[0])
|
||||||
|
}
|
||||||
|
if changes[0].Destination != "" {
|
||||||
|
t.Fatalf("Change destination should be empty, %+v", changes[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
||||||
// systems and ensures that one of its properties is valid.
|
// systems and ensures that one of its properties is valid.
|
||||||
func TestGetUnitProperties(t *testing.T) {
|
func TestGetUnitProperties(t *testing.T) {
|
||||||
@@ -139,6 +175,20 @@ func TestGetUnitProperties(t *testing.T) {
|
|||||||
if names[0] != "system.slice" {
|
if names[0] != "system.slice" {
|
||||||
t.Fatal("unexpected wants for /")
|
t.Fatal("unexpected wants for /")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop, err := conn.GetUnitProperty(unit, "Wants")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prop.Name != "Wants" {
|
||||||
|
t.Fatal("unexpected property name")
|
||||||
|
}
|
||||||
|
|
||||||
|
val := prop.Value.Value().([]string)
|
||||||
|
if !reflect.DeepEqual(val, names) {
|
||||||
|
t.Fatal("unexpected property value")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
||||||
@@ -150,10 +200,37 @@ func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
|||||||
unit := "//invalid#$^/"
|
unit := "//invalid#$^/"
|
||||||
|
|
||||||
_, err := conn.GetUnitProperties(unit)
|
_, err := conn.GetUnitProperties(unit)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("Expected an error, got nil")
|
t.Fatal("Expected an error, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = conn.GetUnitProperty(unit, "Wants")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected an error, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSetUnitProperties changes a cgroup setting on the `tmp.mount`
|
||||||
|
// which should exist on all systemd systems and ensures that the
|
||||||
|
// property was set.
|
||||||
|
func TestSetUnitProperties(t *testing.T) {
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
unit := "tmp.mount"
|
||||||
|
|
||||||
|
if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := conn.GetUnitTypeProperties(unit, "Mount")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := info["CPUShares"].(uint64)
|
||||||
|
if value != 1023 {
|
||||||
|
t.Fatal("CPUShares of unit is not 1023, %s", value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that basic transient unit starting and stopping works.
|
// Ensure that basic transient unit starting and stopping works.
|
||||||
@@ -211,3 +288,27 @@ func TestStartStopTransientUnit(t *testing.T) {
|
|||||||
t.Fatalf("Test unit found in list, should be stopped")
|
t.Fatalf("Test unit found in list, should be stopped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnJobListener(t *testing.T) {
|
||||||
|
target := "start-stop.service"
|
||||||
|
conn := setupConn(t)
|
||||||
|
|
||||||
|
setupUnit(target, conn, t)
|
||||||
|
|
||||||
|
jobSize := len(conn.jobListener.jobs)
|
||||||
|
|
||||||
|
_, err := conn.StartUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.StopUnit(target, "replace")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentJobSize := len(conn.jobListener.jobs)
|
||||||
|
if jobSize != currentJobSize {
|
||||||
|
t.Fatal("JobListener jobs leaked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -209,3 +209,12 @@ func PropPropagatesReloadTo(units ...string) Property {
|
|||||||
func PropRequiresMountsFor(units ...string) Property {
|
func PropRequiresMountsFor(units ...string) Property {
|
||||||
return propDependency("RequiresMountsFor", units)
|
return propDependency("RequiresMountsFor", units)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PropSlice sets the Slice unit property. See
|
||||||
|
// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
|
||||||
|
func PropSlice(slice string) Property {
|
||||||
|
return Property{
|
||||||
|
Name: "Slice",
|
||||||
|
Value: dbus.MakeVariant(slice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user