Skip to content

Commit f376d66

Browse files
committed
pkg/login1: initial commit
This commit adds login1 package, which is a small subset of github.com/coreos/go-systemd/v22/login1 package with ability to use shared D-Bus connection and with proper error handling for Reboot method call, which is not yet provided by the upstream. The idea is to use this package in favor of github.com/coreos/go-systemd in agent code responsible for rebooting the node. However, this requires tests in agent code, so it will be done in the next step. See coreos/go-systemd#387 for more details. Signed-off-by: Mateusz Gozdek <[email protected]>
1 parent 373e9b1 commit f376d66

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

pkg/login1/login1.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Package login1 is a small subset of github.com/coreos/go-systemd/v22/login1 package with
2+
// ability to use shared D-Bus connection and with proper error handling for Reboot method call, which
3+
// is not yet provided by the upstream.
4+
package login1
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
godbus "github.com/godbus/dbus/v5"
11+
)
12+
13+
const (
14+
// DBusDest is an object path used by systemd-logind.
15+
DBusDest = "org.freedesktop.login1"
16+
// DBusInterface is an systemd-logind intefrace name.
17+
DBusInterface = "org.freedesktop.login1.Manager"
18+
// DBusPath is a standard path to systemd-logind interface.
19+
DBusPath = "/org/freedesktop/login1"
20+
// DBusMethodNameReboot is a login1 manager interface method name responsible for rebooting.
21+
DBusMethodNameReboot = "Reboot"
22+
)
23+
24+
// Client describes functionality of provided login1 client.
25+
type Client interface {
26+
Reboot(context.Context) error
27+
}
28+
29+
// Objector describes functionality required from given D-Bus connection.
30+
type Objector interface {
31+
Object(string, godbus.ObjectPath) godbus.BusObject
32+
}
33+
34+
// Caller describes required functionality from D-Bus object.
35+
type Caller interface {
36+
CallWithContext(ctx context.Context, method string, flags godbus.Flags, args ...interface{}) *godbus.Call
37+
}
38+
39+
type rebooter struct {
40+
caller Caller
41+
}
42+
43+
// New creates new login1 client using given D-Bus connection.
44+
func New(objector Objector) (Client, error) {
45+
if objector == nil {
46+
return nil, fmt.Errorf("no objector given")
47+
}
48+
49+
return &rebooter{
50+
caller: objector.Object(DBusDest, DBusPath),
51+
}, nil
52+
}
53+
54+
// Reboot reboots machine on which it's called.
55+
func (r *rebooter) Reboot(ctx context.Context) error {
56+
if call := r.caller.CallWithContext(ctx, DBusInterface+"."+DBusMethodNameReboot, 0, false); call.Err != nil {
57+
return fmt.Errorf("calling reboot: %w", call.Err)
58+
}
59+
60+
return nil
61+
}

pkg/login1/login1_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package login1_test
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"testing"
8+
9+
godbus "github.com/godbus/dbus/v5"
10+
11+
"github.com/flatcar-linux/flatcar-linux-update-operator/pkg/dbus"
12+
"github.com/flatcar-linux/flatcar-linux-update-operator/pkg/login1"
13+
)
14+
15+
func Test_Creating_new_client_returns_error_when_no_objector_is_given(t *testing.T) {
16+
t.Parallel()
17+
18+
client, err := login1.New(nil)
19+
if err == nil {
20+
t.Fatalf("Expected error creating client with no connector")
21+
}
22+
23+
if client != nil {
24+
t.Fatalf("Expected client to be nil when New returns error")
25+
}
26+
}
27+
28+
func Test_Rebooting(t *testing.T) {
29+
t.Parallel()
30+
31+
t.Run("use_given_context_for_D-Bus_call", func(t *testing.T) {
32+
t.Parallel()
33+
34+
testKey := struct{}{}
35+
expectedValue := "bar"
36+
37+
ctx := context.WithValue(context.Background(), testKey, expectedValue)
38+
39+
connectionWithContextCheck := &dbus.MockConnection{
40+
ObjectF: func(string, godbus.ObjectPath) godbus.BusObject {
41+
return &dbus.MockObject{
42+
CallWithContextF: func(ctx context.Context, method string, flags godbus.Flags, args ...interface{}) *godbus.Call {
43+
if val := ctx.Value(testKey); val != expectedValue {
44+
t.Fatalf("Got unexpected context on call")
45+
}
46+
47+
return &godbus.Call{}
48+
},
49+
}
50+
},
51+
}
52+
53+
client, err := login1.New(connectionWithContextCheck)
54+
if err != nil {
55+
t.Fatalf("Unexpected error creating client: %v", err)
56+
}
57+
58+
if err := client.Reboot(ctx); err != nil {
59+
t.Fatalf("Unexpected error rebooting: %v", err)
60+
}
61+
})
62+
63+
t.Run("returns_error_when_D-Bus_call_fails", func(t *testing.T) {
64+
t.Parallel()
65+
66+
expectedError := fmt.Errorf("reboot error")
67+
68+
connectionWithFailingObjectCall := &dbus.MockConnection{
69+
ObjectF: func(string, godbus.ObjectPath) godbus.BusObject {
70+
return &dbus.MockObject{
71+
CallWithContextF: func(ctx context.Context, method string, flags godbus.Flags, args ...interface{}) *godbus.Call {
72+
return &godbus.Call{
73+
Err: expectedError,
74+
}
75+
},
76+
}
77+
},
78+
}
79+
80+
client, err := login1.New(connectionWithFailingObjectCall)
81+
if err != nil {
82+
t.Fatalf("Unexpected error creating client: %v", err)
83+
}
84+
85+
if err := client.Reboot(context.Background()); !errors.Is(err, expectedError) {
86+
t.Fatalf("Unexpected error rebooting: %v", err)
87+
}
88+
})
89+
}

0 commit comments

Comments
 (0)