Skip to content

Commit 36f8eef

Browse files
committed
Add nodeLabeller interface for testing
Signed-off-by: Evan Lezar <[email protected]>
1 parent 6544a19 commit 36f8eef

File tree

4 files changed

+238
-27
lines changed

4 files changed

+238
-27
lines changed

pkg/mig/reconfigure/api.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ type migParted interface {
3030
applyMIGModeOnly() error
3131
applyMIGConfig() error
3232
}
33+
34+
// nodeLabeller defines an interface for interacting with node labels.
35+
//
36+
//go:generate moq -rm -fmt=goimports -out node-labeller_mock.go . nodeLabeller
37+
type nodeLabeller interface {
38+
getNodeLabelValue(string) (string, error)
39+
setNodeLabelValue(string, string) error
40+
}

pkg/mig/reconfigure/node-labeller_mock.go

Lines changed: 124 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/mig/reconfigure/reconfigure.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929

3030
log "github.com/sirupsen/logrus"
3131
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/client-go/kubernetes"
3233
)
3334

3435
const (
@@ -41,8 +42,9 @@ const (
4142

4243
type reconfigurer struct {
4344
*reconfigureMIGOptions
44-
migParted migParted
4545
commandRunner
46+
migParted migParted
47+
node nodeLabeller
4648
}
4749

4850
// A commandWithOutput runs a command and ensures that STDERR and STDOUT are
@@ -74,6 +76,10 @@ func New(opts ...Option) (Reconfigurer, error) {
7476
DriverLibraryPath: o.DriverLibraryPath,
7577
commandRunner: c,
7678
},
79+
node: &node{
80+
clientset: o.clientset,
81+
name: o.NodeName,
82+
},
7783
}
7884

7985
return r, nil
@@ -91,7 +97,7 @@ func (opts *reconfigurer) Reconfigure() error {
9197
}
9298

9399
log.Infof("Getting current value of the '%s' node label", opts.ConfigStateLabel)
94-
state, err := opts.getNodeLabelValue(opts.ConfigStateLabel)
100+
state, err := opts.node.getNodeLabelValue(opts.ConfigStateLabel)
95101
if err != nil {
96102
return fmt.Errorf("unable to get the value of the %q label: %w", opts.ConfigStateLabel, err)
97103
}
@@ -142,7 +148,7 @@ func (opts *reconfigurer) Reconfigure() error {
142148
if err := opts.migParted.applyMIGModeOnly(); err != nil || opts.migParted.assertMIGModeOnly() != nil {
143149
if opts.WithReboot {
144150
log.Infof("Changing the '%s' node label to '%s'", opts.ConfigStateLabel, configStateRebooting)
145-
if err := opts.setNodeLabelValue(opts.ConfigStateLabel, configStateRebooting); err != nil {
151+
if err := opts.node.setNodeLabelValue(opts.ConfigStateLabel, configStateRebooting); err != nil {
146152
log.Errorf("Unable to set the value of '%s' to '%s'", opts.ConfigStateLabel, configStateRebooting)
147153
log.Error("Exiting so as not to reboot multiple times unexpectedly")
148154
return fmt.Errorf("unable to set the value of %q to %q: %w", opts.ConfigStateLabel, configStateRebooting, err)
@@ -393,8 +399,13 @@ func rebootHost(hostRootMount string) error {
393399
return nil
394400
}
395401

396-
func (opts *reconfigureMIGOptions) getNodeLabelValue(label string) (string, error) {
397-
node, err := opts.clientset.CoreV1().Nodes().Get(context.TODO(), opts.NodeName, metav1.GetOptions{})
402+
type node struct {
403+
clientset *kubernetes.Clientset
404+
name string
405+
}
406+
407+
func (n *node) getNodeLabelValue(label string) (string, error) {
408+
node, err := n.clientset.CoreV1().Nodes().Get(context.TODO(), n.name, metav1.GetOptions{})
398409
if err != nil {
399410
return "", fmt.Errorf("unable to get node object: %w", err)
400411
}
@@ -407,16 +418,16 @@ func (opts *reconfigureMIGOptions) getNodeLabelValue(label string) (string, erro
407418
return value, nil
408419
}
409420

410-
func (opts *reconfigureMIGOptions) setNodeLabelValue(label, value string) error {
411-
node, err := opts.clientset.CoreV1().Nodes().Get(context.TODO(), opts.NodeName, metav1.GetOptions{})
421+
func (n *node) setNodeLabelValue(label, value string) error {
422+
node, err := n.clientset.CoreV1().Nodes().Get(context.TODO(), n.name, metav1.GetOptions{})
412423
if err != nil {
413424
return fmt.Errorf("unable to get node object: %w", err)
414425
}
415426

416427
labels := node.GetLabels()
417428
labels[label] = value
418429
node.SetLabels(labels)
419-
_, err = opts.clientset.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{})
430+
_, err = n.clientset.CoreV1().Nodes().Update(context.TODO(), node, metav1.UpdateOptions{})
420431
if err != nil {
421432
return fmt.Errorf("unable to update node object: %w", err)
422433
}

pkg/mig/reconfigure/reconfigure_test.go

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,36 @@ func (c *commandRunnerWithCLI) Run(cmd *exec.Cmd) error {
3535
return c.mock.Run(cmd)
3636
}
3737

38+
type nodeWithLabels struct {
39+
mock *nodeLabellerMock
40+
setLabels map[string]string
41+
}
42+
43+
func (n *nodeWithLabels) getNodeLabelValue(label string) (string, error) {
44+
return n.mock.getNodeLabelValue(label)
45+
}
46+
47+
func (n *nodeWithLabels) setNodeLabelValue(label string, value string) error {
48+
if err := n.mock.setNodeLabelValue(label, value); err != nil {
49+
return err
50+
}
51+
if n.setLabels == nil {
52+
n.setLabels = make(map[string]string)
53+
}
54+
n.setLabels[label] = value
55+
return nil
56+
}
57+
3858
func TestReconfigure(t *testing.T) {
3959
testCases := []struct {
40-
description string
41-
options reconfigureMIGOptions
42-
commandRunner *commandRunnerWithCLI
43-
migParted *migPartedMock
44-
checkMigParted func(*migPartedMock)
45-
expectedError error
46-
expectedCalls [][]string
60+
description string
61+
options reconfigureMIGOptions
62+
migParted *migPartedMock
63+
checkMigParted func(*migPartedMock)
64+
nodeLabeller *nodeWithLabels
65+
checkNodeLabeller func(*nodeWithLabels)
66+
expectedError error
67+
expectedCalls [][]string
4768
}{
4869
{
4970
description: "mig assert valid config failure does not call commands",
@@ -53,13 +74,7 @@ func TestReconfigure(t *testing.T) {
5374
SelectedMIGConfig: "selected-mig-config",
5475
DriverLibraryPath: "/path/to/libnvidia-ml.so.1",
5576
HostRootMount: "/host/",
56-
},
57-
commandRunner: &commandRunnerWithCLI{
58-
mock: &commandRunnerMock{
59-
RunFunc: func(cmd *exec.Cmd) error {
60-
return fmt.Errorf("error running command %v", cmd.Path)
61-
},
62-
},
77+
ConfigStateLabel: "example.com/config.state",
6378
},
6479
migParted: &migPartedMock{
6580
assertValidMIGConfigFunc: func() error {
@@ -76,9 +91,53 @@ func TestReconfigure(t *testing.T) {
7691
expectedError: fmt.Errorf("error validating the selected MIG configuration: invalid mig config"),
7792
expectedCalls: nil,
7893
},
94+
{
95+
description: "node label error is causes exit",
96+
options: reconfigureMIGOptions{
97+
NodeName: "NodeName",
98+
MIGPartedConfigFile: "/path/to/config/file.yaml",
99+
SelectedMIGConfig: "selected-mig-config",
100+
DriverLibraryPath: "/path/to/libnvidia-ml.so.1",
101+
HostRootMount: "/host/",
102+
ConfigStateLabel: "example.com/config.state",
103+
},
104+
migParted: &migPartedMock{
105+
assertValidMIGConfigFunc: func() error {
106+
return nil
107+
},
108+
},
109+
checkMigParted: func(mpm *migPartedMock) {
110+
require.Len(t, mpm.calls.assertValidMIGConfig, 1)
111+
require.Len(t, mpm.calls.applyMIGConfig, 0)
112+
require.Len(t, mpm.calls.assertMIGModeOnly, 0)
113+
require.Len(t, mpm.calls.applyMIGModeOnly, 0)
114+
require.Len(t, mpm.calls.applyMIGConfig, 0)
115+
},
116+
nodeLabeller: &nodeWithLabels{
117+
mock: &nodeLabellerMock{
118+
getNodeLabelValueFunc: func(s string) (string, error) {
119+
return "", fmt.Errorf("error getting label")
120+
},
121+
},
122+
},
123+
checkNodeLabeller: func(nwl *nodeWithLabels) {
124+
calls := nwl.mock.getNodeLabelValueCalls()
125+
require.Len(t, calls, 1)
126+
require.EqualValues(t, []struct{ S string }{{"example.com/config.state"}}, calls)
127+
},
128+
expectedError: fmt.Errorf(`unable to get the value of the "example.com/config.state" label: error getting label`),
129+
},
79130
}
80131

81132
for _, tc := range testCases {
133+
commandRunner := &commandRunnerWithCLI{
134+
mock: &commandRunnerMock{
135+
RunFunc: func(cmd *exec.Cmd) error {
136+
return fmt.Errorf("error running command %v", cmd.Path)
137+
},
138+
},
139+
}
140+
82141
t.Run(tc.description, func(t *testing.T) {
83142
// TODO: Once we have better mocks in place for the following
84143
// functionality, we can update this.
@@ -91,17 +150,26 @@ func TestReconfigure(t *testing.T) {
91150

92151
r := &reconfigurer{
93152
reconfigureMIGOptions: &tc.options,
94-
commandRunner: tc.commandRunner,
153+
commandRunner: commandRunner,
95154
migParted: tc.migParted,
155+
node: tc.nodeLabeller,
96156
}
97157

98158
err := r.Reconfigure()
99-
require.EqualValues(t, tc.expectedError.Error(), err.Error())
100-
101-
tc.checkMigParted(tc.migParted)
159+
if tc.expectedError == nil {
160+
require.NoError(t, err)
161+
} else {
162+
require.EqualError(t, err, tc.expectedError.Error())
163+
}
102164

103-
require.EqualValues(t, tc.expectedCalls, tc.commandRunner.calls)
165+
if tc.checkMigParted != nil {
166+
tc.checkMigParted(tc.migParted)
167+
}
168+
if tc.checkNodeLabeller != nil {
169+
tc.checkNodeLabeller(tc.nodeLabeller)
170+
}
104171

172+
require.EqualValues(t, tc.expectedCalls, commandRunner.calls)
105173
})
106174
}
107175
}

0 commit comments

Comments
 (0)