Skip to content

Commit d260c44

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 62257ee commit d260c44

File tree

2 files changed

+259
-203
lines changed

2 files changed

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

0 commit comments

Comments
 (0)