Skip to content

Commit d38c8dc

Browse files
committed
Namespaced network object end2end tests
Move `[sriov] operator No SriovNetworkNodePolicy ...` test cases to its own test file. Implement test case to verify controller and webhook logic Signed-off-by: Andrea Panattoni <[email protected]>
1 parent 98b8c16 commit d38c8dc

File tree

2 files changed

+258
-202
lines changed

2 files changed

+258
-202
lines changed
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package tests
2+
3+
import (
4+
"context"
5+
"strings"
6+
"time"
7+
8+
. "github.com/onsi/ginkgo/v2"
9+
. "github.com/onsi/gomega"
10+
11+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
14+
15+
netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
16+
17+
sriovv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
18+
"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/cluster"
19+
"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/discovery"
20+
"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/namespaces"
21+
"github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/nodes"
22+
)
23+
24+
var _ = Describe("[sriov] operator", Ordered, ContinueOnFailure, func() {
25+
Describe("No SriovNetworkNodePolicy", func() {
26+
Context("SR-IOV network config daemon can be set by nodeselector", func() {
27+
// 26186
28+
It("Should schedule the config daemon on selected nodes", func() {
29+
if discovery.Enabled() {
30+
Skip("Test unsuitable to be run in discovery mode")
31+
}
32+
33+
By("Checking that a daemon is scheduled on each worker node")
34+
Eventually(func() bool {
35+
return daemonsScheduledOnNodes("node-role.kubernetes.io/worker=")
36+
}, 3*time.Minute, 1*time.Second).Should(Equal(true))
37+
38+
By("Labeling one worker node with the label needed for the daemon")
39+
allNodes, err := clients.CoreV1Interface.Nodes().List(context.Background(), metav1.ListOptions{
40+
LabelSelector: "node-role.kubernetes.io/worker",
41+
})
42+
Expect(err).ToNot(HaveOccurred())
43+
44+
selectedNodes, err := nodes.MatchingOptionalSelector(clients, allNodes.Items)
45+
Expect(err).ToNot(HaveOccurred())
46+
47+
Expect(len(selectedNodes)).To(BeNumerically(">", 0), "There must be at least one worker")
48+
candidate := selectedNodes[0]
49+
candidate.Labels["sriovenabled"] = "true"
50+
_, err = clients.CoreV1Interface.Nodes().Update(context.Background(), &candidate, metav1.UpdateOptions{})
51+
Expect(err).ToNot(HaveOccurred())
52+
53+
By("Setting the node selector for each daemon")
54+
cfg := sriovv1.SriovOperatorConfig{}
55+
err = clients.Get(context.TODO(), runtimeclient.ObjectKey{
56+
Name: "default",
57+
Namespace: operatorNamespace,
58+
}, &cfg)
59+
Expect(err).ToNot(HaveOccurred())
60+
cfg.Spec.ConfigDaemonNodeSelector = map[string]string{
61+
"sriovenabled": "true",
62+
}
63+
Eventually(func() error {
64+
return clients.Update(context.TODO(), &cfg)
65+
}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
66+
67+
By("Checking that a daemon is scheduled only on selected node")
68+
Eventually(func() bool {
69+
return !daemonsScheduledOnNodes("sriovenabled!=true") &&
70+
daemonsScheduledOnNodes("sriovenabled=true")
71+
}, 1*time.Minute, 1*time.Second).Should(Equal(true))
72+
73+
By("Restoring the node selector for daemons")
74+
err = clients.Get(context.TODO(), runtimeclient.ObjectKey{
75+
Name: "default",
76+
Namespace: operatorNamespace,
77+
}, &cfg)
78+
Expect(err).ToNot(HaveOccurred())
79+
cfg.Spec.ConfigDaemonNodeSelector = map[string]string{}
80+
Eventually(func() error {
81+
return clients.Update(context.TODO(), &cfg)
82+
}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
83+
84+
By("Checking that a daemon is scheduled on each worker node")
85+
Eventually(func() bool {
86+
return daemonsScheduledOnNodes("node-role.kubernetes.io/worker")
87+
}, 1*time.Minute, 1*time.Second).Should(Equal(true))
88+
})
89+
})
90+
91+
Context("LogLevel affects operator's logs", func() {
92+
It("when set to 0 no lifecycle logs are present", func() {
93+
if discovery.Enabled() {
94+
Skip("Test unsuitable to be run in discovery mode")
95+
}
96+
97+
initialLogLevelValue := getOperatorConfigLogLevel()
98+
DeferCleanup(func() {
99+
By("Restore LogLevel to its initial value")
100+
setOperatorConfigLogLevel(initialLogLevelValue)
101+
})
102+
103+
initialDisableDrain, err := cluster.GetNodeDrainState(clients, operatorNamespace)
104+
Expect(err).ToNot(HaveOccurred())
105+
106+
DeferCleanup(func() {
107+
By("Restore DisableDrain to its initial value")
108+
Eventually(func() error {
109+
return cluster.SetDisableNodeDrainState(clients, operatorNamespace, initialDisableDrain)
110+
}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
111+
})
112+
113+
By("Set operator LogLevel to 2")
114+
setOperatorConfigLogLevel(2)
115+
116+
By("Flip DisableDrain to trigger operator activity")
117+
since := time.Now().Add(-10 * time.Second)
118+
Eventually(func() error {
119+
return cluster.SetDisableNodeDrainState(clients, operatorNamespace, !initialDisableDrain)
120+
}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
121+
122+
By("Assert logs contains verbose output")
123+
Eventually(func(g Gomega) {
124+
logs := getOperatorLogs(since)
125+
g.Expect(logs).To(
126+
ContainElement(And(
127+
ContainSubstring("Reconciling SriovOperatorConfig"),
128+
)),
129+
)
130+
131+
// Should contain verbose logging
132+
g.Expect(logs).To(
133+
ContainElement(
134+
ContainSubstring("Start to sync webhook objects"),
135+
),
136+
)
137+
}, 1*time.Minute, 5*time.Second).Should(Succeed())
138+
139+
By("Reduce operator LogLevel to 0")
140+
setOperatorConfigLogLevel(0)
141+
142+
By("Flip DisableDrain again to trigger operator activity")
143+
since = time.Now().Add(-10 * time.Second)
144+
Eventually(func() error {
145+
return cluster.SetDisableNodeDrainState(clients, operatorNamespace, initialDisableDrain)
146+
}, 1*time.Minute, 5*time.Second).ShouldNot(HaveOccurred())
147+
148+
By("Assert logs contains less operator activity")
149+
Eventually(func(g Gomega) {
150+
logs := getOperatorLogs(since)
151+
152+
// time only contains sec, but we can have race here that in the same sec there was a sync
153+
afterLogs := []string{}
154+
found := false
155+
for _, log := range logs {
156+
if found {
157+
afterLogs = append(afterLogs, log)
158+
}
159+
if strings.Contains(log, "{\"new-level\": 0, \"current-level\": 2}") {
160+
found = true
161+
}
162+
}
163+
g.Expect(found).To(BeTrue())
164+
g.Expect(afterLogs).To(
165+
ContainElement(And(
166+
ContainSubstring("Reconciling SriovOperatorConfig"),
167+
)),
168+
)
169+
170+
// Should not contain verbose logging
171+
g.Expect(afterLogs).ToNot(
172+
ContainElement(
173+
ContainSubstring("Start to sync webhook objects"),
174+
),
175+
)
176+
}, 3*time.Minute, 5*time.Second).Should(Succeed())
177+
})
178+
})
179+
180+
Context("SriovNetworkMetricsExporter", func() {
181+
BeforeEach(func() {
182+
if discovery.Enabled() {
183+
Skip("Test unsuitable to be run in discovery mode")
184+
}
185+
186+
initialValue := isFeatureFlagEnabled("metricsExporter")
187+
DeferCleanup(func() {
188+
By("Restoring initial feature flag value")
189+
setFeatureFlag("metricsExporter", initialValue)
190+
})
191+
192+
By("Enabling `metricsExporter` feature flag")
193+
setFeatureFlag("metricsExporter", true)
194+
})
195+
196+
It("should be deployed if the feature gate is enabled", func() {
197+
By("Checking that a daemon is scheduled on selected node")
198+
Eventually(func() bool {
199+
return isDaemonsetScheduledOnNodes("node-role.kubernetes.io/worker", "app=sriov-network-metrics-exporter")
200+
}).WithTimeout(time.Minute).WithPolling(time.Second).Should(Equal(true))
201+
})
202+
203+
It("should deploy ServiceMonitor if the Prometheus operator is installed", func() {
204+
_, err := clients.ServiceMonitors(operatorNamespace).List(context.Background(), metav1.ListOptions{})
205+
if k8serrors.IsNotFound(err) {
206+
Skip("Prometheus operator not available in the cluster")
207+
}
208+
209+
By("Checking ServiceMonitor is deployed if needed")
210+
Eventually(func(g Gomega) {
211+
_, err := clients.ServiceMonitors(operatorNamespace).Get(context.Background(), "sriov-network-metrics-exporter", metav1.GetOptions{})
212+
g.Expect(err).ToNot(HaveOccurred())
213+
}).WithTimeout(time.Minute).WithPolling(time.Second).Should(Succeed())
214+
})
215+
216+
It("should remove ServiceMonitor when the feature is turned off", func() {
217+
setFeatureFlag("metricsExporter", false)
218+
Eventually(func(g Gomega) {
219+
_, err := clients.ServiceMonitors(operatorNamespace).Get(context.Background(), "sriov-network-metrics-exporter", metav1.GetOptions{})
220+
g.Expect(k8serrors.IsNotFound(err)).To(BeTrue())
221+
}).WithTimeout(time.Minute).WithPolling(time.Second).Should(Succeed())
222+
})
223+
})
224+
225+
Context("Namespaced network objects", func() {
226+
DescribeTable("can be create in every namespaces", func(object runtimeclient.Object) {
227+
err := clients.Create(context.Background(), object)
228+
Expect(err).ToNot(HaveOccurred())
229+
230+
waitForNetAttachDef(object.GetName(), object.GetNamespace())
231+
232+
err = clients.Delete(context.Background(), object)
233+
Expect(err).ToNot(HaveOccurred())
234+
235+
Eventually(func() bool {
236+
netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
237+
err := clients.Get(context.Background(), runtimeclient.ObjectKey{Name: object.GetName(), Namespace: object.GetNamespace()}, netAttDef)
238+
return err != nil && k8serrors.IsNotFound(err)
239+
}, 2*time.Minute, 10*time.Second).Should(BeTrue())
240+
},
241+
Entry("SriovNetwork", &sriovv1.SriovNetwork{ObjectMeta: metav1.ObjectMeta{Name: "sriovnet1", Namespace: namespaces.Test}}),
242+
Entry("SriovIBNetwork", &sriovv1.SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Name: "sriovibnet1", Namespace: namespaces.Test}}),
243+
Entry("OVSNetwork", &sriovv1.OVSNetwork{ObjectMeta: metav1.ObjectMeta{Name: "ovsnet1", Namespace: namespaces.Test}}),
244+
)
245+
246+
DescribeTable("can NOT be in application namespace and have .Spec.NetworkNamespace != ''", func(object runtimeclient.Object) {
247+
err := clients.Create(context.Background(), object)
248+
Expect(err).To(HaveOccurred())
249+
Expect(string(k8serrors.ReasonForError(err))).
250+
To(ContainSubstring(".Spec.NetworkNamespace field can't be specified if the resource is not in the "))
251+
},
252+
Entry("SriovNetwork", &sriovv1.SriovNetwork{ObjectMeta: metav1.ObjectMeta{Name: "sriovnet1", Namespace: namespaces.Test}, Spec: sriovv1.SriovNetworkSpec{NetworkNamespace: "default"}}),
253+
Entry("SriovIBNetwork", &sriovv1.SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Name: "sriovibnet1", Namespace: namespaces.Test}, Spec: sriovv1.SriovIBNetworkSpec{NetworkNamespace: "default"}}),
254+
Entry("OVSNetwork", &sriovv1.OVSNetwork{ObjectMeta: metav1.ObjectMeta{Name: "ovsnet1", Namespace: namespaces.Test}, Spec: sriovv1.OVSNetworkSpec{NetworkNamespace: "default"}}),
255+
)
256+
})
257+
})
258+
})

0 commit comments

Comments
 (0)