Skip to content

Commit 936ef00

Browse files
committed
fix: skip isRdma mutation for vfio-pci with InfiniBand linkType
The mutating webhook unconditionally set isRdma to true for policies with InfiniBand link type, even when deviceType was vfio-pci. This caused the validating webhook to reject the resource since deviceType vfio-pci conflicts with isRdma: true. Check the deviceType before applying the InfiniBand isRdma patch and skip it when deviceType is vfio-pci. Add unit tests for mutateSriovNetworkNodePolicy covering default values, InfiniBand with various device types, Ethernet, and preservation of user-specified fields. Signed-off-by: Sebastian Sch <sebassch@gmail.com>
1 parent 0756e35 commit 936ef00

File tree

2 files changed

+191
-7
lines changed

2 files changed

+191
-7
lines changed

pkg/webhook/mutate.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,25 @@ func mutateSriovNetworkNodePolicy(cr map[string]interface{}) (*v1.AdmissionRespo
3030
}
3131

3232
patchs := []map[string]interface{}{}
33-
spec := cr["spec"]
34-
if _, ok := spec.(map[string]interface{})["priority"]; !ok {
33+
specMap, ok := cr["spec"].(map[string]interface{})
34+
if !ok {
35+
return &reviewResponse, nil
36+
}
37+
if _, ok := specMap["priority"]; !ok {
3538
log.Log.V(2).Info("mutateSriovNetworkNodePolicy(): set default priority to lowest for", "policy-name", name)
3639
patchs = append(patchs, defaultPriorityPatch)
3740
}
38-
if _, ok := spec.(map[string]interface{})["isRdma"]; !ok {
41+
if _, ok := specMap["isRdma"]; !ok {
3942
log.Log.V(2).Info("mutateSriovNetworkNodePolicy(): set default isRdma to false for policy", "policy-name", name)
4043
patchs = append(patchs, defaultIsRdmaPatch)
4144
}
42-
// Device with InfiniBand link type requires isRdma to be true
43-
if str, ok := spec.(map[string]interface{})["linkType"].(string); ok && strings.EqualFold(str, constants.LinkTypeIB) {
44-
log.Log.V(2).Info("mutateSriovNetworkNodePolicy(): set isRdma to true for policy since ib link type is detected", "policy-name", name)
45-
patchs = append(patchs, InfiniBandIsRdmaPatch)
45+
// Device with InfiniBand link type requires isRdma to be true, except for vfio-pci device type
46+
if str, ok := specMap["linkType"].(string); ok && strings.EqualFold(str, constants.LinkTypeIB) {
47+
devType, _ := specMap["deviceType"].(string)
48+
if !strings.EqualFold(devType, constants.DeviceTypeVfioPci) {
49+
log.Log.V(2).Info("mutateSriovNetworkNodePolicy(): set isRdma to true for policy since ib link type is detected", "policy-name", name)
50+
patchs = append(patchs, InfiniBandIsRdmaPatch)
51+
}
4652
}
4753
var err error
4854
reviewResponse.Patch, err = json.Marshal(patchs)

pkg/webhook/mutate_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package webhook
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
. "github.com/onsi/gomega"
8+
9+
constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
10+
)
11+
12+
func buildPolicyMap(name string, spec map[string]interface{}) map[string]interface{} {
13+
return map[string]interface{}{
14+
"metadata": map[string]interface{}{
15+
"name": name,
16+
},
17+
"spec": spec,
18+
}
19+
}
20+
21+
func patchesFromResponse(g *GomegaWithT, resp []byte) []map[string]interface{} {
22+
var patches []map[string]interface{}
23+
err := json.Unmarshal(resp, &patches)
24+
g.Expect(err).ToNot(HaveOccurred())
25+
return patches
26+
}
27+
28+
func hasPatch(patches []map[string]interface{}, path string, value interface{}) bool {
29+
for _, p := range patches {
30+
if p["path"] == path && p["value"] == value {
31+
return true
32+
}
33+
}
34+
return false
35+
}
36+
37+
func TestMutateSriovNetworkNodePolicy_DefaultPolicy(t *testing.T) {
38+
g := NewGomegaWithT(t)
39+
cr := buildPolicyMap(constants.DefaultPolicyName, map[string]interface{}{})
40+
41+
resp, err := mutateSriovNetworkNodePolicy(cr)
42+
g.Expect(err).ToNot(HaveOccurred())
43+
g.Expect(resp.Allowed).To(BeTrue())
44+
g.Expect(resp.Patch).To(BeNil())
45+
}
46+
47+
func TestMutateSriovNetworkNodePolicy_DefaultValues(t *testing.T) {
48+
g := NewGomegaWithT(t)
49+
cr := buildPolicyMap("test-policy", map[string]interface{}{})
50+
51+
resp, err := mutateSriovNetworkNodePolicy(cr)
52+
g.Expect(err).ToNot(HaveOccurred())
53+
g.Expect(resp.Allowed).To(BeTrue())
54+
55+
patches := patchesFromResponse(g, resp.Patch)
56+
g.Expect(hasPatch(patches, "/spec/priority", float64(99))).To(BeTrue())
57+
g.Expect(hasPatch(patches, "/spec/isRdma", false)).To(BeTrue())
58+
g.Expect(hasPatch(patches, "/spec/isRdma", true)).To(BeFalse())
59+
}
60+
61+
func TestMutateSriovNetworkNodePolicy_InfiniBandSetsIsRdma(t *testing.T) {
62+
g := NewGomegaWithT(t)
63+
cr := buildPolicyMap("ib-policy", map[string]interface{}{
64+
"linkType": constants.LinkTypeIB,
65+
})
66+
67+
resp, err := mutateSriovNetworkNodePolicy(cr)
68+
g.Expect(err).ToNot(HaveOccurred())
69+
g.Expect(resp.Allowed).To(BeTrue())
70+
71+
patches := patchesFromResponse(g, resp.Patch)
72+
g.Expect(hasPatch(patches, "/spec/isRdma", true)).To(BeTrue())
73+
}
74+
75+
func TestMutateSriovNetworkNodePolicy_InfiniBandNetdeviceSetsIsRdma(t *testing.T) {
76+
g := NewGomegaWithT(t)
77+
cr := buildPolicyMap("ib-netdevice-policy", map[string]interface{}{
78+
"linkType": constants.LinkTypeIB,
79+
"deviceType": constants.DeviceTypeNetDevice,
80+
})
81+
82+
resp, err := mutateSriovNetworkNodePolicy(cr)
83+
g.Expect(err).ToNot(HaveOccurred())
84+
g.Expect(resp.Allowed).To(BeTrue())
85+
86+
patches := patchesFromResponse(g, resp.Patch)
87+
g.Expect(hasPatch(patches, "/spec/isRdma", true)).To(BeTrue())
88+
}
89+
90+
func TestMutateSriovNetworkNodePolicy_InfiniBandVfioPciDoesNotSetIsRdma(t *testing.T) {
91+
g := NewGomegaWithT(t)
92+
cr := buildPolicyMap("ib-vfio-policy", map[string]interface{}{
93+
"linkType": constants.LinkTypeIB,
94+
"deviceType": constants.DeviceTypeVfioPci,
95+
})
96+
97+
resp, err := mutateSriovNetworkNodePolicy(cr)
98+
g.Expect(err).ToNot(HaveOccurred())
99+
g.Expect(resp.Allowed).To(BeTrue())
100+
101+
patches := patchesFromResponse(g, resp.Patch)
102+
g.Expect(hasPatch(patches, "/spec/isRdma", true)).To(BeFalse(),
103+
"isRdma should NOT be set to true when deviceType is vfio-pci")
104+
g.Expect(hasPatch(patches, "/spec/isRdma", false)).To(BeTrue(),
105+
"isRdma should default to false when deviceType is vfio-pci with IB linkType")
106+
}
107+
108+
func TestMutateSriovNetworkNodePolicy_EthernetDoesNotSetIsRdma(t *testing.T) {
109+
g := NewGomegaWithT(t)
110+
cr := buildPolicyMap("eth-policy", map[string]interface{}{
111+
"linkType": constants.LinkTypeETH,
112+
})
113+
114+
resp, err := mutateSriovNetworkNodePolicy(cr)
115+
g.Expect(err).ToNot(HaveOccurred())
116+
g.Expect(resp.Allowed).To(BeTrue())
117+
118+
patches := patchesFromResponse(g, resp.Patch)
119+
g.Expect(hasPatch(patches, "/spec/isRdma", true)).To(BeFalse())
120+
g.Expect(hasPatch(patches, "/spec/isRdma", false)).To(BeTrue())
121+
}
122+
123+
func TestMutateSriovNetworkNodePolicy_ExistingIsRdmaNotOverridden(t *testing.T) {
124+
g := NewGomegaWithT(t)
125+
cr := buildPolicyMap("existing-rdma-policy", map[string]interface{}{
126+
"isRdma": true,
127+
})
128+
129+
resp, err := mutateSriovNetworkNodePolicy(cr)
130+
g.Expect(err).ToNot(HaveOccurred())
131+
g.Expect(resp.Allowed).To(BeTrue())
132+
133+
patches := patchesFromResponse(g, resp.Patch)
134+
g.Expect(hasPatch(patches, "/spec/isRdma", false)).To(BeFalse(),
135+
"should not override existing isRdma value")
136+
}
137+
138+
func TestMutateSriovNetworkNodePolicy_MissingSpecDoesNotPanic(t *testing.T) {
139+
g := NewGomegaWithT(t)
140+
cr := map[string]interface{}{
141+
"metadata": map[string]interface{}{
142+
"name": "no-spec-policy",
143+
},
144+
}
145+
146+
resp, err := mutateSriovNetworkNodePolicy(cr)
147+
g.Expect(err).ToNot(HaveOccurred())
148+
g.Expect(resp.Allowed).To(BeTrue())
149+
}
150+
151+
func TestMutateSriovNetworkNodePolicy_NilSpecDoesNotPanic(t *testing.T) {
152+
g := NewGomegaWithT(t)
153+
cr := map[string]interface{}{
154+
"metadata": map[string]interface{}{
155+
"name": "nil-spec-policy",
156+
},
157+
"spec": nil,
158+
}
159+
160+
resp, err := mutateSriovNetworkNodePolicy(cr)
161+
g.Expect(err).ToNot(HaveOccurred())
162+
g.Expect(resp.Allowed).To(BeTrue())
163+
}
164+
165+
func TestMutateSriovNetworkNodePolicy_ExistingPriorityNotOverridden(t *testing.T) {
166+
g := NewGomegaWithT(t)
167+
cr := buildPolicyMap("existing-priority-policy", map[string]interface{}{
168+
"priority": 10,
169+
})
170+
171+
resp, err := mutateSriovNetworkNodePolicy(cr)
172+
g.Expect(err).ToNot(HaveOccurred())
173+
g.Expect(resp.Allowed).To(BeTrue())
174+
175+
patches := patchesFromResponse(g, resp.Patch)
176+
g.Expect(hasPatch(patches, "/spec/priority", float64(99))).To(BeFalse(),
177+
"should not override existing priority value")
178+
}

0 commit comments

Comments
 (0)