mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Add a mockable dbus interface to pkg/util
This commit is contained in:
		
							
								
								
									
										133
									
								
								pkg/util/dbus/dbus.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								pkg/util/dbus/dbus.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
			
		||||
 | 
			
		||||
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 (
 | 
			
		||||
	godbus "github.com/godbus/dbus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Interface is an interface that presents a subset of the godbus/dbus API.  Use this
 | 
			
		||||
// when you want to inject fakeable/mockable D-Bus behavior.
 | 
			
		||||
type Interface interface {
 | 
			
		||||
	// SystemBus returns a connection to the system bus, connecting to it
 | 
			
		||||
	// first if necessary
 | 
			
		||||
	SystemBus() (Connection, error)
 | 
			
		||||
	// SessionBus returns a connection to the session bus, connecting to it
 | 
			
		||||
	// first if necessary
 | 
			
		||||
	SessionBus() (Connection, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Connection represents a D-Bus connection
 | 
			
		||||
type Connection interface {
 | 
			
		||||
	// Returns an Object representing the bus itself
 | 
			
		||||
	BusObject() Object
 | 
			
		||||
 | 
			
		||||
	// Object creates a representation of a remote D-Bus object
 | 
			
		||||
	Object(name, path string) Object
 | 
			
		||||
 | 
			
		||||
	// Signal registers or unregisters a channel to receive D-Bus signals
 | 
			
		||||
	Signal(ch chan<- *godbus.Signal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Object represents a remote D-Bus object
 | 
			
		||||
type Object interface {
 | 
			
		||||
	// Call synchronously calls a D-Bus method
 | 
			
		||||
	Call(method string, flags godbus.Flags, args ...interface{}) Call
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Call represents a pending or completed D-Bus method call
 | 
			
		||||
type Call interface {
 | 
			
		||||
	// Store returns a completed call's return values, or an error
 | 
			
		||||
	Store(retvalues ...interface{}) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements Interface in terms of actually talking to D-Bus
 | 
			
		||||
type dbusImpl struct {
 | 
			
		||||
	systemBus  *connImpl
 | 
			
		||||
	sessionBus *connImpl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements Connection as a godbus.Conn
 | 
			
		||||
type connImpl struct {
 | 
			
		||||
	conn *godbus.Conn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements Object as a godbus.Object
 | 
			
		||||
type objectImpl struct {
 | 
			
		||||
	object *godbus.Object
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements Call as a godbus.Call
 | 
			
		||||
type callImpl struct {
 | 
			
		||||
	call *godbus.Call
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a new Interface which will use godbus to talk to D-Bus
 | 
			
		||||
func New() Interface {
 | 
			
		||||
	return &dbusImpl{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SystemBus is part of Interface
 | 
			
		||||
func (db *dbusImpl) SystemBus() (Connection, error) {
 | 
			
		||||
	if db.systemBus == nil {
 | 
			
		||||
		bus, err := godbus.SystemBus()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		db.systemBus = &connImpl{bus}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db.systemBus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SessionBus is part of Interface
 | 
			
		||||
func (db *dbusImpl) SessionBus() (Connection, error) {
 | 
			
		||||
	if db.sessionBus == nil {
 | 
			
		||||
		bus, err := godbus.SessionBus()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		db.sessionBus = &connImpl{bus}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db.sessionBus, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BusObject is part of the Connection interface
 | 
			
		||||
func (conn *connImpl) BusObject() Object {
 | 
			
		||||
	return &objectImpl{conn.conn.BusObject()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Object is part of the Connection interface
 | 
			
		||||
func (conn *connImpl) Object(name, path string) Object {
 | 
			
		||||
	return &objectImpl{conn.conn.Object(name, godbus.ObjectPath(path))}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signal is part of the Connection interface
 | 
			
		||||
func (conn *connImpl) Signal(ch chan<- *godbus.Signal) {
 | 
			
		||||
	conn.conn.Signal(ch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Call is part of the Object interface
 | 
			
		||||
func (obj *objectImpl) Call(method string, flags godbus.Flags, args ...interface{}) Call {
 | 
			
		||||
	return &callImpl{obj.object.Call(method, flags, args...)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store is part of the Call interface
 | 
			
		||||
func (call *callImpl) Store(retvalues ...interface{}) error {
 | 
			
		||||
	return call.call.Store(retvalues...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										249
									
								
								pkg/util/dbus/dbus_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								pkg/util/dbus/dbus_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	godbus "github.com/godbus/dbus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DBusNameFlagAllowReplacement uint32 = 1 << (iota + 1)
 | 
			
		||||
	DBusNameFlagReplaceExisting
 | 
			
		||||
	DBusNameFlagDoNotQueue
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DBusRequestNameReplyPrimaryOwner uint32 = iota + 1
 | 
			
		||||
	DBusRequestNameReplyInQueue
 | 
			
		||||
	DBusRequestNameReplyExists
 | 
			
		||||
	DBusRequestNameReplyAlreadyOwner
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DBusReleaseNameReplyReleased uint32 = iota + 1
 | 
			
		||||
	DBusReleaseNameReplyNonExistent
 | 
			
		||||
	DBusReleaseNameReplyNotOwner
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func doDBusTest(t *testing.T, dbus Interface, real bool) {
 | 
			
		||||
	bus, err := dbus.SystemBus()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !real {
 | 
			
		||||
			t.Errorf("dbus.SystemBus() failed with fake Interface")
 | 
			
		||||
		}
 | 
			
		||||
		t.Skipf("D-Bus is not running: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	busObj := bus.BusObject()
 | 
			
		||||
 | 
			
		||||
	id := ""
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.GetId", 0).Store(&id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("expected success, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(id) == 0 {
 | 
			
		||||
		t.Errorf("expected non-empty Id, got \"\"")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Switch to the session bus for the rest, since the system bus is more
 | 
			
		||||
	// locked down (and thus harder to trick into emitting signals).
 | 
			
		||||
 | 
			
		||||
	bus, err = dbus.SessionBus()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !real {
 | 
			
		||||
			t.Errorf("dbus.SystemBus() failed with fake Interface")
 | 
			
		||||
		}
 | 
			
		||||
		t.Skipf("D-Bus session bus is not available: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	busObj = bus.BusObject()
 | 
			
		||||
 | 
			
		||||
	name := fmt.Sprintf("io.kubernetes.dbus_test_%d", os.Getpid())
 | 
			
		||||
	owner := ""
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("expected '%s' to be un-owned, but found owner %s", name, owner)
 | 
			
		||||
	}
 | 
			
		||||
	dbuserr, ok := err.(godbus.Error)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		t.Errorf("expected godbus.Error, but got %#v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if dbuserr.Name != "org.freedesktop.DBus.Error.NameHasNoOwner" {
 | 
			
		||||
		t.Errorf("expected NameHasNoOwner error but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sigchan := make(chan *godbus.Signal, 10)
 | 
			
		||||
	bus.Signal(sigchan)
 | 
			
		||||
 | 
			
		||||
	rule := fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", name)
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.AddMatch", 0, rule).Store()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("expected success, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var ret uint32
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.RequestName", 0, name, DBusNameFlagDoNotQueue).Store(&ret)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("expected success, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if ret != DBusRequestNameReplyPrimaryOwner {
 | 
			
		||||
		t.Errorf("expected %v, got %v", DBusRequestNameReplyPrimaryOwner, ret)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&owner)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("expected success, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var changedSignal, acquiredSignal, lostSignal *godbus.Signal
 | 
			
		||||
 | 
			
		||||
	sig1 := <-sigchan
 | 
			
		||||
	sig2 := <-sigchan
 | 
			
		||||
	// We get two signals, but the order isn't guaranteed
 | 
			
		||||
	if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" {
 | 
			
		||||
		changedSignal = sig1
 | 
			
		||||
		acquiredSignal = sig2
 | 
			
		||||
	} else {
 | 
			
		||||
		acquiredSignal = sig1
 | 
			
		||||
		changedSignal = sig2
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if acquiredSignal.Sender != "org.freedesktop.DBus" || acquiredSignal.Name != "org.freedesktop.DBus.NameAcquired" {
 | 
			
		||||
		t.Errorf("expected NameAcquired signal, got %v", acquiredSignal)
 | 
			
		||||
	}
 | 
			
		||||
	acquiredName := acquiredSignal.Body[0].(string)
 | 
			
		||||
	if acquiredName != name {
 | 
			
		||||
		t.Errorf("unexpected NameAcquired arguments: %v", acquiredSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" {
 | 
			
		||||
		t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changedName := changedSignal.Body[0].(string)
 | 
			
		||||
	oldOwner := changedSignal.Body[1].(string)
 | 
			
		||||
	newOwner := changedSignal.Body[2].(string)
 | 
			
		||||
	if changedName != name || oldOwner != "" || newOwner != owner {
 | 
			
		||||
		t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&ret)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("expected success, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if ret != DBusReleaseNameReplyReleased {
 | 
			
		||||
		t.Errorf("expected %v, got %v", DBusReleaseNameReplyReleased, ret)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sig1 = <-sigchan
 | 
			
		||||
	sig2 = <-sigchan
 | 
			
		||||
	if sig1.Name == "org.freedesktop.DBus.NameOwnerChanged" {
 | 
			
		||||
		changedSignal = sig1
 | 
			
		||||
		lostSignal = sig2
 | 
			
		||||
	} else {
 | 
			
		||||
		lostSignal = sig1
 | 
			
		||||
		changedSignal = sig2
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lostSignal.Sender != "org.freedesktop.DBus" || lostSignal.Name != "org.freedesktop.DBus.NameLost" {
 | 
			
		||||
		t.Errorf("expected NameLost signal, got %v", lostSignal)
 | 
			
		||||
	}
 | 
			
		||||
	lostName := lostSignal.Body[0].(string)
 | 
			
		||||
	if lostName != name {
 | 
			
		||||
		t.Errorf("unexpected NameLost arguments: %v", lostSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if changedSignal.Sender != "org.freedesktop.DBus" || changedSignal.Name != "org.freedesktop.DBus.NameOwnerChanged" {
 | 
			
		||||
		t.Errorf("expected NameOwnerChanged signal, got %v", changedSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	changedName = changedSignal.Body[0].(string)
 | 
			
		||||
	oldOwner = changedSignal.Body[1].(string)
 | 
			
		||||
	newOwner = changedSignal.Body[2].(string)
 | 
			
		||||
	if changedName != name || oldOwner != owner || newOwner != "" {
 | 
			
		||||
		t.Errorf("unexpected NameOwnerChanged arguments: %v", changedSignal)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(sigchan) != 0 {
 | 
			
		||||
		t.Errorf("unexpected extra signals (%d)", len(sigchan))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Unregister sigchan
 | 
			
		||||
	bus.Signal(sigchan)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRealDBus(t *testing.T) {
 | 
			
		||||
	dbus := New()
 | 
			
		||||
	doDBusTest(t, dbus, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFakeDBus(t *testing.T) {
 | 
			
		||||
	uniqueName := ":1.1"
 | 
			
		||||
	ownedName := ""
 | 
			
		||||
 | 
			
		||||
	fakeSystem := NewFakeConnection()
 | 
			
		||||
	fakeSystem.SetBusObject(
 | 
			
		||||
		func(method string, args ...interface{}) ([]interface{}, error) {
 | 
			
		||||
			if method == "org.freedesktop.DBus.GetId" {
 | 
			
		||||
				return []interface{}{"foo"}, nil
 | 
			
		||||
			}
 | 
			
		||||
			return nil, fmt.Errorf("unexpected method call '%s'", method)
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	fakeSession := NewFakeConnection()
 | 
			
		||||
	fakeSession.SetBusObject(
 | 
			
		||||
		func(method string, args ...interface{}) ([]interface{}, error) {
 | 
			
		||||
			if method == "org.freedesktop.DBus.GetNameOwner" {
 | 
			
		||||
				checkName := args[0].(string)
 | 
			
		||||
				if checkName != ownedName {
 | 
			
		||||
					return nil, godbus.Error{"org.freedesktop.DBus.Error.NameHasNoOwner", nil}
 | 
			
		||||
				} else {
 | 
			
		||||
					return []interface{}{uniqueName}, nil
 | 
			
		||||
				}
 | 
			
		||||
			} else if method == "org.freedesktop.DBus.RequestName" {
 | 
			
		||||
				reqName := args[0].(string)
 | 
			
		||||
				_ = args[1].(uint32)
 | 
			
		||||
				if ownedName != "" {
 | 
			
		||||
					return []interface{}{DBusRequestNameReplyAlreadyOwner}, nil
 | 
			
		||||
				}
 | 
			
		||||
				ownedName = reqName
 | 
			
		||||
				fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired", reqName)
 | 
			
		||||
				fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, "", uniqueName)
 | 
			
		||||
				return []interface{}{DBusRequestNameReplyPrimaryOwner}, nil
 | 
			
		||||
			} else if method == "org.freedesktop.DBus.ReleaseName" {
 | 
			
		||||
				reqName := args[0].(string)
 | 
			
		||||
				if reqName != ownedName {
 | 
			
		||||
					return []interface{}{DBusReleaseNameReplyNotOwner}, nil
 | 
			
		||||
				}
 | 
			
		||||
				ownedName = ""
 | 
			
		||||
				fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", reqName, uniqueName, "")
 | 
			
		||||
				fakeSession.EmitSignal("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameLost", reqName)
 | 
			
		||||
				return []interface{}{DBusReleaseNameReplyReleased}, nil
 | 
			
		||||
			} else if method == "org.freedesktop.DBus.AddMatch" {
 | 
			
		||||
				return nil, nil
 | 
			
		||||
			} else {
 | 
			
		||||
				return nil, fmt.Errorf("unexpected method call '%s'", method)
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	dbus := NewFake(fakeSystem, fakeSession)
 | 
			
		||||
	doDBusTest(t, dbus, false)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								pkg/util/dbus/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								pkg/util/dbus/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
			
		||||
 | 
			
		||||
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 provides an injectable interface and implementations for D-Bus communication
 | 
			
		||||
package dbus
 | 
			
		||||
							
								
								
									
										135
									
								
								pkg/util/dbus/fake_dbus.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								pkg/util/dbus/fake_dbus.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2015 The Kubernetes Authors All rights reserved.
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
	godbus "github.com/godbus/dbus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DBusFake is a simple fake Interface type.
 | 
			
		||||
type DBusFake struct {
 | 
			
		||||
	systemBus  *DBusFakeConnection
 | 
			
		||||
	sessionBus *DBusFakeConnection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DBusFakeConnection represents a fake D-Bus connection
 | 
			
		||||
type DBusFakeConnection struct {
 | 
			
		||||
	busObject      *fakeObject
 | 
			
		||||
	objects        map[string]*fakeObject
 | 
			
		||||
	signalHandlers []chan<- *godbus.Signal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DBusFakeHandler is used to handle fake D-Bus method calls
 | 
			
		||||
type DBusFakeHandler func(method string, args ...interface{}) ([]interface{}, error)
 | 
			
		||||
 | 
			
		||||
type fakeObject struct {
 | 
			
		||||
	handler DBusFakeHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fakeCall struct {
 | 
			
		||||
	ret []interface{}
 | 
			
		||||
	err error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFake returns a new Interface which will fake talking to D-Bus
 | 
			
		||||
func NewFake(systemBus *DBusFakeConnection, sessionBus *DBusFakeConnection) *DBusFake {
 | 
			
		||||
	return &DBusFake{systemBus, sessionBus}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFakeConnection() *DBusFakeConnection {
 | 
			
		||||
	return &DBusFakeConnection{
 | 
			
		||||
		objects: make(map[string]*fakeObject),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SystemBus is part of Interface
 | 
			
		||||
func (db *DBusFake) SystemBus() (Connection, error) {
 | 
			
		||||
	if db.systemBus != nil {
 | 
			
		||||
		return db.systemBus, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, fmt.Errorf("DBus is not running")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SessionBus is part of Interface
 | 
			
		||||
func (db *DBusFake) SessionBus() (Connection, error) {
 | 
			
		||||
	if db.sessionBus != nil {
 | 
			
		||||
		return db.sessionBus, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, fmt.Errorf("DBus is not running")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BusObject is part of the Connection interface
 | 
			
		||||
func (conn *DBusFakeConnection) BusObject() Object {
 | 
			
		||||
	return conn.busObject
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Object is part of the Connection interface
 | 
			
		||||
func (conn *DBusFakeConnection) Object(name, path string) Object {
 | 
			
		||||
	return conn.objects[name+path]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signal is part of the Connection interface
 | 
			
		||||
func (conn *DBusFakeConnection) Signal(ch chan<- *godbus.Signal) {
 | 
			
		||||
	for i := range conn.signalHandlers {
 | 
			
		||||
		if conn.signalHandlers[i] == ch {
 | 
			
		||||
			conn.signalHandlers = append(conn.signalHandlers[:i], conn.signalHandlers[i+1:]...)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	conn.signalHandlers = append(conn.signalHandlers, ch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBusObject sets the handler for the BusObject of conn
 | 
			
		||||
func (conn *DBusFakeConnection) SetBusObject(handler DBusFakeHandler) {
 | 
			
		||||
	conn.busObject = &fakeObject{handler}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddObject adds a handler for the Object at name and path
 | 
			
		||||
func (conn *DBusFakeConnection) AddObject(name, path string, handler DBusFakeHandler) {
 | 
			
		||||
	conn.objects[name+path] = &fakeObject{handler}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EmitSignal emits a signal on conn
 | 
			
		||||
func (conn *DBusFakeConnection) EmitSignal(name, path, iface, signal string, args ...interface{}) {
 | 
			
		||||
	sig := &godbus.Signal{
 | 
			
		||||
		Sender: name,
 | 
			
		||||
		Path:   godbus.ObjectPath(path),
 | 
			
		||||
		Name:   iface + "." + signal,
 | 
			
		||||
		Body:   args,
 | 
			
		||||
	}
 | 
			
		||||
	for _, ch := range conn.signalHandlers {
 | 
			
		||||
		ch <- sig
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Call is part of the Object interface
 | 
			
		||||
func (obj *fakeObject) Call(method string, flags godbus.Flags, args ...interface{}) Call {
 | 
			
		||||
	ret, err := obj.handler(method, args...)
 | 
			
		||||
	return &fakeCall{ret, err}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Store is part of the Call interface
 | 
			
		||||
func (call *fakeCall) Store(retvalues ...interface{}) error {
 | 
			
		||||
	if call.err != nil {
 | 
			
		||||
		return call.err
 | 
			
		||||
	}
 | 
			
		||||
	return godbus.Store(call.ret, retvalues...)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user