You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
7.2 KiB
346 lines
7.2 KiB
// 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 dbus
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/godbus/dbus"
|
|
)
|
|
|
|
func setupConn(t *testing.T) *Conn {
|
|
conn, err := New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return conn
|
|
}
|
|
|
|
func findFixture(target string, t *testing.T) string {
|
|
abs, err := filepath.Abs("../fixtures/" + target)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return abs
|
|
}
|
|
|
|
func setupUnit(target string, conn *Conn, t *testing.T) {
|
|
// Blindly stop the unit in case it is running
|
|
conn.StopUnit(target, "replace", nil)
|
|
|
|
// Blindly remove the symlink in case it exists
|
|
targetRun := filepath.Join("/run/systemd/system/", target)
|
|
os.Remove(targetRun)
|
|
}
|
|
|
|
func linkUnit(target string, conn *Conn, t *testing.T) {
|
|
abs := findFixture(target, t)
|
|
fixture := []string{abs}
|
|
|
|
changes, err := conn.LinkUnitFiles(fixture, true, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(changes) < 1 {
|
|
t.Fatalf("Expected one change, got %v", changes)
|
|
}
|
|
|
|
runPath := filepath.Join("/run/systemd/system/", target)
|
|
if changes[0].Filename != runPath {
|
|
t.Fatal("Unexpected target filename")
|
|
}
|
|
}
|
|
|
|
// Ensure that basic unit starting and stopping works.
|
|
func TestStartStopUnit(t *testing.T) {
|
|
target := "start-stop.service"
|
|
conn := setupConn(t)
|
|
|
|
setupUnit(target, conn, t)
|
|
linkUnit(target, conn, t)
|
|
|
|
// 2. Start the unit
|
|
reschan := make(chan string)
|
|
_, err := conn.StartUnit(target, "replace", reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
job := <-reschan
|
|
if job != "done" {
|
|
t.Fatal("Job is not done:", job)
|
|
}
|
|
|
|
units, err := conn.ListUnits()
|
|
|
|
var unit *UnitStatus
|
|
for _, u := range units {
|
|
if u.Name == target {
|
|
unit = &u
|
|
}
|
|
}
|
|
|
|
if unit == nil {
|
|
t.Fatalf("Test unit not found in list")
|
|
}
|
|
|
|
if unit.ActiveState != "active" {
|
|
t.Fatalf("Test unit not active")
|
|
}
|
|
|
|
// 3. Stop the unit
|
|
_, err = conn.StopUnit(target, "replace", reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// wait for StopUnit job to complete
|
|
<-reschan
|
|
|
|
units, err = conn.ListUnits()
|
|
|
|
unit = nil
|
|
for _, u := range units {
|
|
if u.Name == target {
|
|
unit = &u
|
|
}
|
|
}
|
|
|
|
if unit != nil {
|
|
t.Fatalf("Test unit found in list, should be stopped")
|
|
}
|
|
}
|
|
|
|
// 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 := findFixture(target, t)
|
|
runPath := filepath.Join("/run/systemd/system/", target)
|
|
|
|
// 1. Enable the unit
|
|
install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if install != false {
|
|
t.Fatal("Install was true")
|
|
}
|
|
|
|
if len(changes) < 1 {
|
|
t.Fatalf("Expected one change, got %v", changes)
|
|
}
|
|
|
|
if changes[0].Filename != runPath {
|
|
t.Fatal("Unexpected target filename")
|
|
}
|
|
|
|
// 2. Disable the unit
|
|
dChanges, err := conn.DisableUnitFiles([]string{abs}, true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(dChanges) != 1 {
|
|
t.Fatalf("Changes should include the path, %v", dChanges)
|
|
}
|
|
if dChanges[0].Filename != runPath {
|
|
t.Fatalf("Change should include correct filename, %+v", dChanges[0])
|
|
}
|
|
if dChanges[0].Destination != "" {
|
|
t.Fatalf("Change destination should be empty, %+v", dChanges[0])
|
|
}
|
|
}
|
|
|
|
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
|
// systems and ensures that one of its properties is valid.
|
|
func TestGetUnitProperties(t *testing.T) {
|
|
conn := setupConn(t)
|
|
|
|
unit := "-.mount"
|
|
|
|
info, err := conn.GetUnitProperties(unit)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
names := info["Wants"].([]string)
|
|
|
|
if len(names) < 1 {
|
|
t.Fatal("/ is unwanted")
|
|
}
|
|
|
|
if names[0] != "system.slice" {
|
|
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
|
|
// unit with an invalid name. This test should be run with --test.timeout set,
|
|
// as a fail will manifest as GetUnitProperties hanging indefinitely.
|
|
func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
|
conn := setupConn(t)
|
|
|
|
unit := "//invalid#$^/"
|
|
|
|
_, err := conn.GetUnitProperties(unit)
|
|
if err == 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:", value)
|
|
}
|
|
}
|
|
|
|
// Ensure that basic transient unit starting and stopping works.
|
|
func TestStartStopTransientUnit(t *testing.T) {
|
|
conn := setupConn(t)
|
|
|
|
props := []Property{
|
|
PropExecStart([]string{"/bin/sleep", "400"}, false),
|
|
}
|
|
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
|
|
|
|
// Start the unit
|
|
reschan := make(chan string)
|
|
_, err := conn.StartTransientUnit(target, "replace", props, reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
job := <-reschan
|
|
if job != "done" {
|
|
t.Fatal("Job is not done:", job)
|
|
}
|
|
|
|
units, err := conn.ListUnits()
|
|
|
|
var unit *UnitStatus
|
|
for _, u := range units {
|
|
if u.Name == target {
|
|
unit = &u
|
|
}
|
|
}
|
|
|
|
if unit == nil {
|
|
t.Fatalf("Test unit not found in list")
|
|
}
|
|
|
|
if unit.ActiveState != "active" {
|
|
t.Fatalf("Test unit not active")
|
|
}
|
|
|
|
// 3. Stop the unit
|
|
_, err = conn.StopUnit(target, "replace", reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// wait for StopUnit job to complete
|
|
<-reschan
|
|
|
|
units, err = conn.ListUnits()
|
|
|
|
unit = nil
|
|
for _, u := range units {
|
|
if u.Name == target {
|
|
unit = &u
|
|
}
|
|
}
|
|
|
|
if unit != nil {
|
|
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)
|
|
linkUnit(target, conn, t)
|
|
|
|
jobSize := len(conn.jobListener.jobs)
|
|
|
|
reschan := make(chan string)
|
|
_, err := conn.StartUnit(target, "replace", reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-reschan
|
|
|
|
_, err = conn.StopUnit(target, "replace", reschan)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-reschan
|
|
|
|
currentJobSize := len(conn.jobListener.jobs)
|
|
if jobSize != currentJobSize {
|
|
t.Fatal("JobListener jobs leaked")
|
|
}
|
|
}
|