Skip to content

Commit 0a2b7a9

Browse files
committed
test(vm): add unit tests for vm-controller
Signed-off-by: Zespre Chang <zespre.chang@suse.com> (cherry picked from commit 33b1df6)
1 parent a8b916c commit 0a2b7a9

File tree

8 files changed

+520
-49
lines changed

8 files changed

+520
-49
lines changed

pkg/controller/vm/common.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package vm
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
"k8s.io/apimachinery/pkg/labels"
6+
kubevirtv1 "kubevirt.io/api/core/v1"
7+
8+
networkv1 "github.com/harvester/vm-dhcp-controller/pkg/apis/network.harvesterhci.io/v1alpha1"
9+
)
10+
11+
func prepareVmNetCfg(vm *kubevirtv1.VirtualMachine, ncm map[string]networkv1.NetworkConfig) *networkv1.VirtualMachineNetworkConfig {
12+
sets := labels.Set{
13+
vmLabelKey: vm.Name,
14+
}
15+
16+
ncs := make([]networkv1.NetworkConfig, 0, len(ncm))
17+
for _, nc := range ncm {
18+
ncs = append(ncs, nc)
19+
}
20+
21+
return &networkv1.VirtualMachineNetworkConfig{
22+
ObjectMeta: metav1.ObjectMeta{
23+
Labels: sets,
24+
Name: vm.Name,
25+
Namespace: vm.Namespace,
26+
OwnerReferences: []metav1.OwnerReference{
27+
{
28+
APIVersion: vm.APIVersion,
29+
Kind: vm.Kind,
30+
Name: vm.Name,
31+
UID: vm.UID,
32+
},
33+
},
34+
},
35+
Spec: networkv1.VirtualMachineNetworkConfigSpec{
36+
VMName: vm.Name,
37+
NetworkConfigs: ncs,
38+
},
39+
}
40+
}
41+
42+
type vmBuilder struct {
43+
vm *kubevirtv1.VirtualMachine
44+
}
45+
46+
func newVMBuilder(namespace, name string) *vmBuilder {
47+
return &vmBuilder{
48+
vm: &kubevirtv1.VirtualMachine{
49+
ObjectMeta: metav1.ObjectMeta{
50+
Namespace: namespace,
51+
Name: name,
52+
},
53+
},
54+
}
55+
}
56+
57+
// WithInterface adds a network interface to the VM with the specified MAC address, NIC name, and network name.
58+
func (b *vmBuilder) WithInterface(macAddress, nicName string) *vmBuilder {
59+
if b.vm.Spec.Template == nil {
60+
b.vm.Spec.Template = &kubevirtv1.VirtualMachineInstanceTemplateSpec{}
61+
}
62+
63+
b.vm.Spec.Template.Spec.Domain.Devices.Interfaces = append(b.vm.Spec.Template.Spec.Domain.Devices.Interfaces, kubevirtv1.Interface{
64+
Name: nicName,
65+
MacAddress: macAddress,
66+
})
67+
68+
return b
69+
}
70+
71+
// WithNetwork adds a network configuration to the VM.
72+
// If networkName is empty, it defaults to a Pod network.
73+
func (b *vmBuilder) WithNetwork(nicName, networkName string) *vmBuilder {
74+
if b.vm.Spec.Template == nil {
75+
b.vm.Spec.Template = &kubevirtv1.VirtualMachineInstanceTemplateSpec{}
76+
}
77+
78+
var ns kubevirtv1.NetworkSource
79+
if networkName == "" {
80+
ns = kubevirtv1.NetworkSource{
81+
Pod: &kubevirtv1.PodNetwork{},
82+
}
83+
} else {
84+
ns = kubevirtv1.NetworkSource{
85+
Multus: &kubevirtv1.MultusNetwork{
86+
NetworkName: networkName,
87+
},
88+
}
89+
}
90+
91+
b.vm.Spec.Template.Spec.Networks = append(b.vm.Spec.Template.Spec.Networks, kubevirtv1.Network{
92+
Name: nicName,
93+
NetworkSource: ns,
94+
})
95+
96+
return b
97+
}
98+
99+
func (b *vmBuilder) Build() *kubevirtv1.VirtualMachine {
100+
return b.vm
101+
}

pkg/controller/vm/controller.go

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"github.com/sirupsen/logrus"
88
corev1 "k8s.io/api/core/v1"
99
apierrors "k8s.io/apimachinery/pkg/api/errors"
10-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11-
"k8s.io/apimachinery/pkg/labels"
1210
kubevirtv1 "kubevirt.io/api/core/v1"
1311

1412
networkv1 "github.com/harvester/vm-dhcp-controller/pkg/apis/network.harvesterhci.io/v1alpha1"
@@ -143,34 +141,3 @@ func (h *Handler) OnChange(key string, vm *kubevirtv1.VirtualMachine) (*kubevirt
143141

144142
return vm, nil
145143
}
146-
147-
func prepareVmNetCfg(vm *kubevirtv1.VirtualMachine, ncm map[string]networkv1.NetworkConfig) *networkv1.VirtualMachineNetworkConfig {
148-
sets := labels.Set{
149-
vmLabelKey: vm.Name,
150-
}
151-
152-
ncs := make([]networkv1.NetworkConfig, 0, len(ncm))
153-
for _, nc := range ncm {
154-
ncs = append(ncs, nc)
155-
}
156-
157-
return &networkv1.VirtualMachineNetworkConfig{
158-
ObjectMeta: metav1.ObjectMeta{
159-
Labels: sets,
160-
Name: vm.Name,
161-
Namespace: vm.Namespace,
162-
OwnerReferences: []metav1.OwnerReference{
163-
{
164-
APIVersion: vm.APIVersion,
165-
Kind: vm.Kind,
166-
Name: vm.Name,
167-
UID: vm.UID,
168-
},
169-
},
170-
},
171-
Spec: networkv1.VirtualMachineNetworkConfigSpec{
172-
VMName: vm.Name,
173-
NetworkConfigs: ncs,
174-
},
175-
}
176-
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package vm
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
corev1 "k8s.io/api/core/v1"
8+
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
11+
networkv1 "github.com/harvester/vm-dhcp-controller/pkg/apis/network.harvesterhci.io/v1alpha1"
12+
"github.com/harvester/vm-dhcp-controller/pkg/controller/vmnetcfg"
13+
"github.com/harvester/vm-dhcp-controller/pkg/generated/clientset/versioned/fake"
14+
"github.com/harvester/vm-dhcp-controller/pkg/util/fakeclient"
15+
"github.com/harvester/vm-dhcp-controller/pkg/util/fakecontroller"
16+
)
17+
18+
const (
19+
testVMNamespace = "default"
20+
testVMName = "test-vm"
21+
testKey = testVMNamespace + "/" + testVMName
22+
testNADNamespace = "default"
23+
testNADName = "test-nad"
24+
testNetworkName = testNADNamespace + "/" + testNADName
25+
testMACAddress1 = "11:22:33:44:55:66"
26+
testMACAddress2 = "22:33:44:55:66:77"
27+
testIPAddress = "192.168.100.100"
28+
testNICName = "nic1"
29+
testVmNetCfgNamespace = "default"
30+
testVmNetCfgName = "test-vm"
31+
)
32+
33+
func newTestVMBuilder() *vmBuilder {
34+
return newVMBuilder(testVMNamespace, testVMName)
35+
}
36+
37+
func newTestVmNetCfgBuilder() *vmnetcfg.VmNetCfgBuilder {
38+
return vmnetcfg.NewVmNetCfgBuilder(testVmNetCfgNamespace, testVmNetCfgName)
39+
}
40+
41+
func TestHandler_OnChange(t *testing.T) {
42+
t.Run("new vm without mac", func(t *testing.T) {
43+
givenVM := newTestVMBuilder().
44+
WithInterface("", testNICName).
45+
WithNetwork(testNICName, testNetworkName).Build()
46+
47+
clientset := fake.NewSimpleClientset()
48+
err := clientset.Tracker().Add(givenVM)
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
53+
handler := Handler{
54+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
55+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
56+
}
57+
58+
_, err = handler.OnChange(testKey, givenVM)
59+
assert.Nil(t, err)
60+
61+
_, err = handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
62+
assert.NotNil(t, err, "expected error when getting vmnetcfg")
63+
})
64+
65+
t.Run("new vm with mac", func(t *testing.T) {
66+
givenVM := newTestVMBuilder().
67+
WithInterface(testMACAddress1, testNICName).
68+
WithNetwork(testNICName, testNetworkName).Build()
69+
70+
expectedVmNetCfg := newTestVmNetCfgBuilder().
71+
Label(vmLabelKey, testVMName).
72+
OwnerRef(metav1.OwnerReference{
73+
Name: testVMName,
74+
}).
75+
WithVMName(testVMName).
76+
WithNetworkConfig("", testMACAddress1, testNetworkName).Build()
77+
78+
clientset := fake.NewSimpleClientset()
79+
err := clientset.Tracker().Add(givenVM)
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
84+
handler := Handler{
85+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
86+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
87+
}
88+
89+
_, err = handler.OnChange(testKey, givenVM)
90+
assert.Nil(t, err)
91+
92+
vmNetCfg, err := handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
93+
assert.Nil(t, err)
94+
assert.Equal(t, expectedVmNetCfg, vmNetCfg)
95+
})
96+
97+
t.Run("new vm attaching to pod network", func(t *testing.T) {
98+
givenVM := newTestVMBuilder().
99+
WithInterface(testMACAddress1, testNICName).
100+
WithNetwork(testNICName, "").Build()
101+
102+
clientset := fake.NewSimpleClientset()
103+
err := clientset.Tracker().Add(givenVM)
104+
if err != nil {
105+
t.Fatal(err)
106+
}
107+
108+
handler := Handler{
109+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
110+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
111+
}
112+
113+
_, err = handler.OnChange(testKey, givenVM)
114+
assert.Nil(t, err)
115+
116+
_, err = handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
117+
assert.NotNil(t, err)
118+
})
119+
120+
t.Run("vm and vmnetcfg network configs are in-sync", func(t *testing.T) {
121+
givenVM := newTestVMBuilder().
122+
WithInterface(testMACAddress1, testNICName).
123+
WithNetwork(testNICName, testNetworkName).Build()
124+
givenVmNetCfg := newTestVmNetCfgBuilder().
125+
Label(vmLabelKey, testVMName).
126+
WithVMName(testVMName).
127+
WithNetworkConfig("", testMACAddress1, testNetworkName).Build()
128+
129+
clientset := fake.NewSimpleClientset()
130+
err := clientset.Tracker().Add(givenVM)
131+
if err != nil {
132+
t.Fatal(err)
133+
}
134+
err = clientset.Tracker().Add(givenVmNetCfg)
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
139+
handler := Handler{
140+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
141+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
142+
}
143+
144+
_, err = handler.OnChange(testKey, givenVM)
145+
assert.Nil(t, err)
146+
147+
vmNetCfg, err := handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
148+
assert.Nil(t, err)
149+
assert.Equal(t, givenVmNetCfg, vmNetCfg)
150+
})
151+
152+
t.Run("vm and vmnetcfg found inconsistent in network configs should be flagged (first iteration)", func(t *testing.T) {
153+
givenVM := newTestVMBuilder().
154+
WithInterface(testMACAddress2, testNICName).
155+
WithNetwork(testNICName, testNetworkName).Build()
156+
givenVmNetCfg := newTestVmNetCfgBuilder().
157+
Label(vmLabelKey, testVMName).
158+
WithVMName(testVMName).
159+
WithNetworkConfig("", testMACAddress1, testNetworkName).
160+
WithNetworkConfigStatus(testIPAddress, testMACAddress1, testNetworkName, networkv1.AllocatedState).
161+
InSyncedCondition(corev1.ConditionTrue, "", "").Build()
162+
163+
expectedVmNetCfg := newTestVmNetCfgBuilder().
164+
Label(vmLabelKey, testVMName).
165+
WithVMName(testVMName).
166+
WithNetworkConfig("", testMACAddress1, testNetworkName).
167+
WithNetworkConfigStatus(testIPAddress, testMACAddress1, testNetworkName, networkv1.AllocatedState).
168+
InSyncedCondition(corev1.ConditionFalse, "NetworkConfigChanged", "Network configuration of the upstrem virtual machine has been changed").Build()
169+
170+
clientset := fake.NewSimpleClientset()
171+
err := clientset.Tracker().Add(givenVM)
172+
if err != nil {
173+
t.Fatal(err)
174+
}
175+
err = clientset.Tracker().Add(givenVmNetCfg)
176+
if err != nil {
177+
t.Fatal(err)
178+
}
179+
180+
handler := Handler{
181+
vmController: fakecontroller.VirtualMachineController(clientset.KubevirtV1().VirtualMachines),
182+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
183+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
184+
}
185+
186+
_, err = handler.OnChange(testKey, givenVM)
187+
assert.Nil(t, err)
188+
189+
vmNetCfg, err := handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
190+
assert.Nil(t, err)
191+
// The InSynced condition is the only thing we care about in this test
192+
assert.Equal(t, expectedVmNetCfg.Status, vmNetCfg.Status)
193+
})
194+
195+
t.Run("flagged vm and vmnetcfg network configs inconsistency should be synced (second iteration)", func(t *testing.T) {
196+
givenVM := newTestVMBuilder().
197+
WithInterface(testMACAddress2, testNICName).
198+
WithNetwork(testNICName, testNetworkName).Build()
199+
givenVmNetCfg := newTestVmNetCfgBuilder().
200+
Label(vmLabelKey, testVMName).
201+
WithVMName(testVMName).
202+
WithNetworkConfig("", testMACAddress1, testNetworkName).
203+
InSyncedCondition(corev1.ConditionFalse, "NetworkConfigChanged", "Network configuration of the upstrem virtual machine has been changed").Build()
204+
205+
expectedVmNetCfg := newTestVmNetCfgBuilder().
206+
Label(vmLabelKey, testVMName).
207+
WithVMName(testVMName).
208+
WithNetworkConfig("", testMACAddress2, testNetworkName).
209+
InSyncedCondition(corev1.ConditionFalse, "NetworkConfigChanged", "Network configuration of the upstrem virtual machine has been changed").Build()
210+
211+
clientset := fake.NewSimpleClientset()
212+
err := clientset.Tracker().Add(givenVM)
213+
if err != nil {
214+
t.Fatal(err)
215+
}
216+
err = clientset.Tracker().Add(givenVmNetCfg)
217+
if err != nil {
218+
t.Fatal(err)
219+
}
220+
221+
handler := Handler{
222+
vmController: fakecontroller.VirtualMachineController(clientset.KubevirtV1().VirtualMachines),
223+
vmnetcfgCache: fakeclient.VirtualMachineNetworkConfigCache(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
224+
vmnetcfgClient: fakeclient.VirtualMachineNetworkConfigClient(clientset.NetworkV1alpha1().VirtualMachineNetworkConfigs),
225+
}
226+
227+
_, err = handler.OnChange(testKey, givenVM)
228+
assert.Nil(t, err)
229+
230+
vmNetCfg, err := handler.vmnetcfgClient.Get(testVmNetCfgNamespace, testVmNetCfgName, metav1.GetOptions{})
231+
assert.Nil(t, err)
232+
// Spec should be synced with the VM
233+
assert.Equal(t, expectedVmNetCfg, vmNetCfg)
234+
})
235+
}

0 commit comments

Comments
 (0)