Skip to content

Commit 5b80f63

Browse files
phlogistonjohnmergify[bot]
authored andcommitted
integration tests: add test cases for nodeselector and affinity rules
Add some tests that check that node selector values and/or affinity rules are passed on from a SmbCommonConfig to the pods that are created. Signed-off-by: John Mulligan <[email protected]>
1 parent c9b0181 commit 5b80f63

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed

tests/integration/scheduling_test.go

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
//go:build integration
2+
// +build integration
3+
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package integration
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"math/rand"
12+
"path"
13+
14+
"github.com/stretchr/testify/suite"
15+
corev1 "k8s.io/api/core/v1"
16+
"k8s.io/apimachinery/pkg/api/resource"
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/apimachinery/pkg/types"
19+
20+
sambaoperatorv1alpha1 "github.com/samba-in-kubernetes/samba-operator/api/v1alpha1"
21+
"github.com/samba-in-kubernetes/samba-operator/tests/utils/kube"
22+
//"github.com/samba-in-kubernetes/samba-operator/tests/utils/poll"
23+
"github.com/samba-in-kubernetes/samba-operator/tests/utils/smbclient"
24+
)
25+
26+
type CommonSelectorSuite struct {
27+
suite.Suite
28+
29+
commonSources []kube.FileSource
30+
testAuths []smbclient.Auth
31+
destNamespace string
32+
33+
// cached values
34+
tc *kube.TestClient
35+
36+
// testID is a short unique test id, pseudo-randomly generated
37+
testID string
38+
// labeledNode has the special label applied to it
39+
labeledNode string
40+
// numPods to create for our tests
41+
numPods int
42+
}
43+
44+
func (s *CommonSelectorSuite) defaultContext() context.Context {
45+
ctx := testContext()
46+
if s.testID != "" {
47+
ctx = context.WithValue(ctx, TestIDKey, s.testID)
48+
}
49+
return ctx
50+
}
51+
52+
func (s *CommonSelectorSuite) getTestClient() *kube.TestClient {
53+
return s.tc
54+
}
55+
56+
func (s *CommonSelectorSuite) labelANode(ctx context.Context) {
57+
// apply a special label to only one node.
58+
// this test assumes that all linux-amd64 nodes in the test cluster can
59+
// run a samba pod. If not, this test may fail in unexpected ways.
60+
nodesList, err := s.tc.Clientset().CoreV1().Nodes().List(ctx, metav1.ListOptions{
61+
LabelSelector: "kubernetes.io/os=linux,kubernetes.io/arch=amd64,!node-role.kubernetes.io/control-plane",
62+
})
63+
s.Require().NoError(err)
64+
s.Require().Greater(len(nodesList.Items), 0)
65+
idx := rand.Intn(len(nodesList.Items))
66+
targetNode := nodesList.Items[idx]
67+
targetNode.Labels["mytestid"] = s.testID
68+
_, err = s.tc.Clientset().CoreV1().Nodes().Update(
69+
ctx, &targetNode, metav1.UpdateOptions{})
70+
s.Require().NoError(err)
71+
s.labeledNode = targetNode.Name
72+
}
73+
74+
func (s *CommonSelectorSuite) unlabelNodes(ctx context.Context) {
75+
nodesList, err := s.tc.Clientset().CoreV1().Nodes().List(ctx, metav1.ListOptions{
76+
LabelSelector: fmt.Sprintf("mytestid=%s", s.testID),
77+
})
78+
s.Require().NoError(err)
79+
s.Require().Greater(len(nodesList.Items), 0)
80+
for _, node := range nodesList.Items {
81+
delete(node.Labels, "mytestid")
82+
_, err = s.tc.Clientset().CoreV1().Nodes().Update(
83+
ctx, &node, metav1.UpdateOptions{})
84+
s.Require().NoError(err)
85+
}
86+
}
87+
88+
func (s *CommonSelectorSuite) commonConfigName() string {
89+
return "schedtest-" + s.testID
90+
}
91+
92+
func (s *CommonSelectorSuite) deleteSmbCommonConfig(ctx context.Context) {
93+
cc := &sambaoperatorv1alpha1.SmbCommonConfig{}
94+
nn := types.NamespacedName{
95+
Namespace: s.destNamespace,
96+
Name: s.commonConfigName(),
97+
}
98+
err := s.tc.TypedObjectClient().Get(ctx, nn, cc)
99+
s.Require().NoError(err)
100+
err = s.tc.TypedObjectClient().Delete(ctx, cc)
101+
s.Require().NoError(err)
102+
}
103+
104+
func (s *CommonSelectorSuite) smbShareTemplate(idx int) *sambaoperatorv1alpha1.SmbShare {
105+
sname := fmt.Sprintf("schedtest-%s-%d", s.testID, idx)
106+
return &sambaoperatorv1alpha1.SmbShare{
107+
ObjectMeta: metav1.ObjectMeta{
108+
Name: sname,
109+
Namespace: s.destNamespace,
110+
},
111+
Spec: sambaoperatorv1alpha1.SmbShareSpec{
112+
ShareName: sname,
113+
ReadOnly: false,
114+
Browseable: true,
115+
SecurityConfig: "sharesec1",
116+
CommonConfig: s.commonConfigName(),
117+
Storage: sambaoperatorv1alpha1.SmbShareStorageSpec{
118+
Pvc: &sambaoperatorv1alpha1.SmbSharePvcSpec{
119+
Name: sname,
120+
Spec: &corev1.PersistentVolumeClaimSpec{
121+
AccessModes: []corev1.PersistentVolumeAccessMode{
122+
corev1.ReadWriteOnce,
123+
},
124+
Resources: corev1.ResourceRequirements{
125+
Requests: corev1.ResourceList{
126+
corev1.ResourceStorage: resource.MustParse("1Gi"),
127+
},
128+
},
129+
},
130+
},
131+
},
132+
},
133+
}
134+
}
135+
136+
func (s *CommonSelectorSuite) createSmbShares(ctx context.Context) {
137+
for i := 0; i < s.numPods; i++ {
138+
smbShare := s.smbShareTemplate(i)
139+
err := s.tc.TypedObjectClient().Create(ctx, smbShare)
140+
s.Require().NoError(err)
141+
}
142+
}
143+
144+
func (s *CommonSelectorSuite) deleteSmbShares(ctx context.Context) {
145+
for i := 0; i < s.numPods; i++ {
146+
smbShare := s.smbShareTemplate(i)
147+
err := s.tc.TypedObjectClient().Delete(ctx, smbShare)
148+
s.Require().NoError(err)
149+
}
150+
}
151+
152+
func (s *CommonSelectorSuite) podLabelSelector() string {
153+
return "samba-operator.samba.org/service,samba-operator.samba.org/common-config-from=" + s.commonConfigName()
154+
}
155+
156+
func (s *CommonSelectorSuite) getPodFetchOptions() kube.PodFetchOptions {
157+
return kube.PodFetchOptions{
158+
Namespace: s.destNamespace,
159+
LabelSelector: s.podLabelSelector(),
160+
MaxFound: s.numPods,
161+
MinFound: 1,
162+
}
163+
}
164+
165+
type NodeSelectorSuite struct {
166+
CommonSelectorSuite
167+
}
168+
169+
func (s *NodeSelectorSuite) createSmbCommonConfig(ctx context.Context) {
170+
cc := &sambaoperatorv1alpha1.SmbCommonConfig{
171+
ObjectMeta: metav1.ObjectMeta{
172+
Name: s.commonConfigName(),
173+
Namespace: s.destNamespace,
174+
Labels: map[string]string{
175+
"mytestid": s.testID,
176+
},
177+
},
178+
Spec: sambaoperatorv1alpha1.SmbCommonConfigSpec{
179+
PodSettings: &sambaoperatorv1alpha1.SmbCommonConfigPodSettings{
180+
NodeSelector: map[string]string{
181+
"kubernetes.io/os": "linux",
182+
"kubernetes.io/arch": "amd64",
183+
"mytestid": s.testID,
184+
},
185+
},
186+
},
187+
}
188+
err := s.tc.TypedObjectClient().Create(ctx, cc)
189+
s.Require().NoError(err)
190+
}
191+
192+
func (s *NodeSelectorSuite) SetupSuite() {
193+
s.testID = generateTestID()
194+
s.numPods = 4
195+
s.T().Logf("test ID: %s", s.testID)
196+
s.Require().NotEmpty(s.destNamespace)
197+
s.tc = kube.NewTestClient("")
198+
ctx := s.defaultContext()
199+
createFromFiles(ctx, s.Require(), s.tc, s.commonSources)
200+
201+
s.labelANode(ctx)
202+
s.createSmbCommonConfig(ctx)
203+
}
204+
205+
func (s *NodeSelectorSuite) TearDownSuite() {
206+
ctx := s.defaultContext()
207+
s.unlabelNodes(ctx)
208+
s.deleteSmbCommonConfig(ctx)
209+
deleteFromFiles(ctx, s.Require(), s.tc, s.commonSources)
210+
}
211+
212+
func (s *NodeSelectorSuite) TestPodsRunOnLabeledNode() {
213+
ctx := s.defaultContext()
214+
require := s.Require()
215+
216+
s.createSmbShares(ctx)
217+
require.NoError(waitForPodExist(ctx, s), "smb server pods do not exist")
218+
require.NoError(waitForAllPodReady(ctx, s), "smb server pods are not ready")
219+
220+
podList, err := s.tc.Clientset().CoreV1().Pods(s.destNamespace).List(
221+
ctx,
222+
metav1.ListOptions{LabelSelector: s.podLabelSelector()},
223+
)
224+
require.NoError(err)
225+
require.Greater(len(podList.Items), 0)
226+
for _, pod := range podList.Items {
227+
s.T().Logf("pod %s running on: %s", pod.Name, pod.Spec.NodeName)
228+
require.Equal(pod.Spec.NodeName, s.labeledNode, "pod not running on labeled node")
229+
}
230+
}
231+
232+
func (s *NodeSelectorSuite) TearDownTest() {
233+
ctx := s.defaultContext()
234+
s.deleteSmbShares(ctx)
235+
}
236+
237+
type AffinityBasedSelectorSuite struct {
238+
CommonSelectorSuite
239+
}
240+
241+
func (s *AffinityBasedSelectorSuite) createSmbCommonConfig(ctx context.Context) {
242+
cc := &sambaoperatorv1alpha1.SmbCommonConfig{
243+
ObjectMeta: metav1.ObjectMeta{
244+
Name: s.commonConfigName(),
245+
Namespace: s.destNamespace,
246+
Labels: map[string]string{
247+
"mytestid": s.testID,
248+
},
249+
},
250+
Spec: sambaoperatorv1alpha1.SmbCommonConfigSpec{
251+
PodSettings: &sambaoperatorv1alpha1.SmbCommonConfigPodSettings{
252+
Affinity: &corev1.Affinity{
253+
NodeAffinity: &corev1.NodeAffinity{
254+
PreferredDuringSchedulingIgnoredDuringExecution: []corev1.PreferredSchedulingTerm{{
255+
Weight: 10,
256+
Preference: corev1.NodeSelectorTerm{
257+
MatchExpressions: []corev1.NodeSelectorRequirement{{
258+
Key: "mytestid",
259+
Operator: corev1.NodeSelectorOpIn,
260+
Values: []string{s.testID},
261+
}},
262+
},
263+
}},
264+
},
265+
},
266+
},
267+
},
268+
}
269+
err := s.tc.TypedObjectClient().Create(ctx, cc)
270+
s.Require().NoError(err)
271+
}
272+
273+
func (s *AffinityBasedSelectorSuite) SetupSuite() {
274+
s.testID = generateTestID()
275+
s.numPods = 4
276+
s.T().Logf("test ID: %s", s.testID)
277+
s.Require().NotEmpty(s.destNamespace)
278+
s.tc = kube.NewTestClient("")
279+
ctx := s.defaultContext()
280+
createFromFiles(ctx, s.Require(), s.tc, s.commonSources)
281+
282+
s.labelANode(ctx)
283+
s.createSmbCommonConfig(ctx)
284+
}
285+
286+
func (s *AffinityBasedSelectorSuite) TearDownSuite() {
287+
ctx := s.defaultContext()
288+
s.unlabelNodes(ctx)
289+
s.deleteSmbCommonConfig(ctx)
290+
deleteFromFiles(ctx, s.Require(), s.tc, s.commonSources)
291+
}
292+
293+
func (s *AffinityBasedSelectorSuite) TestPodsRunOnLabeledNode() {
294+
ctx := s.defaultContext()
295+
require := s.Require()
296+
297+
s.createSmbShares(ctx)
298+
require.NoError(waitForPodExist(ctx, s), "smb server pods do not exist")
299+
require.NoError(waitForAllPodReady(ctx, s), "smb server pods are not ready")
300+
301+
podList, err := s.tc.Clientset().CoreV1().Pods(s.destNamespace).List(
302+
ctx,
303+
metav1.ListOptions{LabelSelector: s.podLabelSelector()},
304+
)
305+
require.NoError(err)
306+
require.Greater(len(podList.Items), 0)
307+
for _, pod := range podList.Items {
308+
s.T().Logf("pod %s running on: %s", pod.Name, pod.Spec.NodeName)
309+
require.Equal(pod.Spec.NodeName, s.labeledNode, "pod not running on labeled node")
310+
}
311+
}
312+
313+
func (s *AffinityBasedSelectorSuite) TearDownTest() {
314+
ctx := s.defaultContext()
315+
s.deleteSmbShares(ctx)
316+
}
317+
318+
func init() {
319+
schedulingTests := testRoot.ChildPriority("scheduling", 7)
320+
schedulingTests.AddSuite("NodeSelectorSuite", &NodeSelectorSuite{CommonSelectorSuite{
321+
commonSources: []kube.FileSource{
322+
{
323+
Path: path.Join(testFilesDir, "userssecret1.yaml"),
324+
Namespace: testNamespace,
325+
},
326+
{
327+
Path: path.Join(testFilesDir, "smbsecurityconfig1.yaml"),
328+
Namespace: testNamespace,
329+
},
330+
},
331+
destNamespace: testNamespace,
332+
testAuths: []smbclient.Auth{{
333+
Username: "sambauser",
334+
Password: "1nsecurely",
335+
}},
336+
}})
337+
schedulingTests.AddSuite("AffinityBasedSelectorSuite", &AffinityBasedSelectorSuite{CommonSelectorSuite{
338+
commonSources: []kube.FileSource{
339+
{
340+
Path: path.Join(testFilesDir, "userssecret1.yaml"),
341+
Namespace: testNamespace,
342+
},
343+
{
344+
Path: path.Join(testFilesDir, "smbsecurityconfig1.yaml"),
345+
Namespace: testNamespace,
346+
},
347+
},
348+
destNamespace: testNamespace,
349+
testAuths: []smbclient.Auth{{
350+
Username: "sambauser",
351+
Password: "1nsecurely",
352+
}},
353+
}})
354+
}

0 commit comments

Comments
 (0)