Skip to content

Commit 99b0627

Browse files
authored
Merge pull request #8055 from sbueringer/pr-envtest-node-label-sync
🌱 Add envtest unit tests for node label sync
2 parents b1c0044 + 98aac8c commit 99b0627

File tree

1 file changed

+211
-5
lines changed

1 file changed

+211
-5
lines changed

internal/controllers/machine/machine_controller_noderef_test.go

Lines changed: 211 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ limitations under the License.
1717
package machine
1818

1919
import (
20+
"fmt"
2021
"testing"
22+
"time"
2123

2224
. "github.com/onsi/gomega"
2325
corev1 "k8s.io/api/core/v1"
2426
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/utils/pointer"
2528
ctrl "sigs.k8s.io/controller-runtime"
2629
"sigs.k8s.io/controller-runtime/pkg/client"
2730
"sigs.k8s.io/controller-runtime/pkg/handler"
@@ -31,6 +34,7 @@ import (
3134
"sigs.k8s.io/cluster-api/controllers/noderefutil"
3235
"sigs.k8s.io/cluster-api/controllers/remote"
3336
"sigs.k8s.io/cluster-api/util"
37+
"sigs.k8s.io/cluster-api/util/kubeconfig"
3438
)
3539

3640
func TestGetNode(t *testing.T) {
@@ -164,6 +168,200 @@ func TestGetNode(t *testing.T) {
164168
}
165169
}
166170

171+
func TestNodeLabelSync(t *testing.T) {
172+
defaultCluster := &clusterv1.Cluster{
173+
ObjectMeta: metav1.ObjectMeta{
174+
Name: "test-cluster",
175+
Namespace: metav1.NamespaceDefault,
176+
},
177+
}
178+
179+
defaultMachine := clusterv1.Machine{
180+
ObjectMeta: metav1.ObjectMeta{
181+
Name: "machine-test",
182+
Namespace: metav1.NamespaceDefault,
183+
Labels: map[string]string{
184+
clusterv1.MachineControlPlaneLabel: "",
185+
},
186+
},
187+
Spec: clusterv1.MachineSpec{
188+
ClusterName: defaultCluster.Name,
189+
Bootstrap: clusterv1.Bootstrap{
190+
ConfigRef: &corev1.ObjectReference{
191+
APIVersion: "bootstrap.cluster.x-k8s.io/v1beta1",
192+
Kind: "GenericBootstrapConfig",
193+
Name: "bootstrap-config1",
194+
},
195+
},
196+
InfrastructureRef: corev1.ObjectReference{
197+
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
198+
Kind: "GenericInfrastructureMachine",
199+
Name: "infra-config1",
200+
},
201+
},
202+
}
203+
204+
t.Run("Should sync node labels", func(t *testing.T) {
205+
g := NewWithT(t)
206+
207+
ns, err := env.CreateNamespace(ctx, "test-node-label-sync")
208+
g.Expect(err).ToNot(HaveOccurred())
209+
defer func() {
210+
g.Expect(env.Cleanup(ctx, ns)).To(Succeed())
211+
}()
212+
213+
nodeProviderID := fmt.Sprintf("test://%s", util.RandomString(6))
214+
215+
cluster := defaultCluster.DeepCopy()
216+
cluster.Namespace = ns.Name
217+
218+
machine := defaultMachine.DeepCopy()
219+
machine.Namespace = ns.Name
220+
machine.Spec.ProviderID = pointer.String(nodeProviderID)
221+
222+
// Set Machine labels.
223+
machine.Labels = map[string]string{}
224+
// The expectation is that these labels will be synced to the Node.
225+
managedMachineLabels := map[string]string{
226+
clusterv1.NodeRoleLabelPrefix + "/anyRole": "",
227+
228+
clusterv1.ManagedNodeLabelDomain: "valueFromMachine",
229+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain: "valueFromMachine",
230+
clusterv1.ManagedNodeLabelDomain + "/anything": "valueFromMachine",
231+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain + "/anything": "valueFromMachine",
232+
233+
clusterv1.NodeRestrictionLabelDomain: "valueFromMachine",
234+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain: "valueFromMachine",
235+
clusterv1.NodeRestrictionLabelDomain + "/anything": "valueFromMachine",
236+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain + "/anything": "valueFromMachine",
237+
}
238+
for k, v := range managedMachineLabels {
239+
machine.Labels[k] = v
240+
}
241+
// The expectation is that these labels will not be synced to the Node.
242+
unmanagedMachineLabels := map[string]string{
243+
"foo": "",
244+
"bar": "",
245+
"company.xyz/node.cluster.x-k8s.io": "not-managed",
246+
"gpu-node.cluster.x-k8s.io": "not-managed",
247+
"company.xyz/node-restriction.kubernetes.io": "not-managed",
248+
"gpu-node-restriction.kubernetes.io": "not-managed",
249+
}
250+
for k, v := range unmanagedMachineLabels {
251+
machine.Labels[k] = v
252+
}
253+
254+
// Create Node.
255+
node := &corev1.Node{
256+
ObjectMeta: metav1.ObjectMeta{
257+
GenerateName: "machine-test-node-",
258+
},
259+
Spec: corev1.NodeSpec{ProviderID: nodeProviderID},
260+
}
261+
262+
// Set Node labels
263+
// The expectation is that these labels will be overwritten by the labels
264+
// from the Machine by the node label sync.
265+
node.Labels = map[string]string{}
266+
managedNodeLabelsToBeOverWritten := map[string]string{
267+
clusterv1.NodeRoleLabelPrefix + "/anyRole": "valueFromNode",
268+
269+
clusterv1.ManagedNodeLabelDomain: "valueFromNode",
270+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain: "valueFromNode",
271+
clusterv1.ManagedNodeLabelDomain + "/anything": "valueFromNode",
272+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain + "/anything": "valueFromNode",
273+
274+
clusterv1.NodeRestrictionLabelDomain: "valueFromNode",
275+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain: "valueFromNode",
276+
clusterv1.NodeRestrictionLabelDomain + "/anything": "valueFromNode",
277+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain + "/anything": "valueFromNode",
278+
}
279+
for k, v := range managedNodeLabelsToBeOverWritten {
280+
node.Labels[k] = v
281+
}
282+
// The expectation is that these labels will be preserved by the node label sync.
283+
unmanagedNodeLabelsToBePreserved := map[string]string{
284+
"node-role.kubernetes.io/control-plane": "",
285+
"label": "valueFromNode",
286+
}
287+
for k, v := range unmanagedNodeLabelsToBePreserved {
288+
node.Labels[k] = v
289+
}
290+
291+
g.Expect(env.Create(ctx, node)).To(Succeed())
292+
defer func() {
293+
g.Expect(env.Cleanup(ctx, node)).To(Succeed())
294+
}()
295+
296+
g.Expect(env.Create(ctx, cluster)).To(Succeed())
297+
defaultKubeconfigSecret := kubeconfig.GenerateSecret(cluster, kubeconfig.FromEnvTestConfig(env.Config, cluster))
298+
g.Expect(env.Create(ctx, defaultKubeconfigSecret)).To(Succeed())
299+
g.Expect(env.Create(ctx, machine)).To(Succeed())
300+
301+
// Validate that the right labels where synced to the Node.
302+
g.Eventually(func(g Gomega) bool {
303+
if err := env.Get(ctx, client.ObjectKeyFromObject(node), node); err != nil {
304+
return false
305+
}
306+
307+
// Managed Machine Labels should have been synced to the Node.
308+
for k, v := range managedMachineLabels {
309+
g.Expect(node.Labels).To(HaveKeyWithValue(k, v))
310+
}
311+
// Unmanaged Machine labels should not have been synced to the Node.
312+
for k, v := range unmanagedMachineLabels {
313+
g.Expect(node.Labels).ToNot(HaveKeyWithValue(k, v))
314+
}
315+
316+
// Pre-existing managed Node labels should have been overwritten on the Node.
317+
for k, v := range managedNodeLabelsToBeOverWritten {
318+
g.Expect(node.Labels).ToNot(HaveKeyWithValue(k, v))
319+
}
320+
// Pre-existing unmanaged Node labels should have been preserved on the Node.
321+
for k, v := range unmanagedNodeLabelsToBePreserved {
322+
g.Expect(node.Labels).To(HaveKeyWithValue(k, v))
323+
}
324+
325+
return true
326+
}, 10*time.Second).Should(BeTrue())
327+
328+
// Remove managed labels from Machine.
329+
modifiedMachine := machine.DeepCopy()
330+
for k := range managedMachineLabels {
331+
delete(modifiedMachine.Labels, k)
332+
}
333+
g.Expect(env.Patch(ctx, modifiedMachine, client.MergeFrom(machine))).To(Succeed())
334+
335+
// Validate that managed Machine labels were removed from the Node and all others are not changed.
336+
g.Eventually(func(g Gomega) bool {
337+
if err := env.Get(ctx, client.ObjectKeyFromObject(node), node); err != nil {
338+
return false
339+
}
340+
341+
// Managed Machine Labels should have been removed from the Node now.
342+
for k, v := range managedMachineLabels {
343+
g.Expect(node.Labels).ToNot(HaveKeyWithValue(k, v))
344+
}
345+
// Unmanaged Machine labels should not have been synced at all to the Node.
346+
for k, v := range unmanagedMachineLabels {
347+
g.Expect(node.Labels).ToNot(HaveKeyWithValue(k, v))
348+
}
349+
350+
// Pre-existing managed Node labels have been overwritten earlier by the managed Machine labels.
351+
// Now that the managed Machine labels have been removed, they should still not exist.
352+
for k, v := range managedNodeLabelsToBeOverWritten {
353+
g.Expect(node.Labels).ToNot(HaveKeyWithValue(k, v))
354+
}
355+
// Pre-existing unmanaged Node labels should have been preserved on the Node.
356+
for k, v := range unmanagedNodeLabelsToBePreserved {
357+
g.Expect(node.Labels).To(HaveKeyWithValue(k, v))
358+
}
359+
360+
return true
361+
}, 10*time.Second).Should(BeTrue())
362+
})
363+
}
364+
167365
func TestSummarizeNodeConditions(t *testing.T) {
168366
testCases := []struct {
169367
name string
@@ -230,11 +428,19 @@ func TestSummarizeNodeConditions(t *testing.T) {
230428

231429
func TestGetManagedLabels(t *testing.T) {
232430
// Create managedLabels map from known managed prefixes.
233-
managedLabels := map[string]string{}
234-
managedLabels[clusterv1.ManagedNodeLabelDomain] = ""
235-
managedLabels["custom-prefix."+clusterv1.NodeRestrictionLabelDomain] = ""
236-
managedLabels["custom-prefix."+clusterv1.NodeRestrictionLabelDomain+"/anything"] = ""
237-
managedLabels[clusterv1.NodeRoleLabelPrefix+"/anything"] = ""
431+
managedLabels := map[string]string{
432+
clusterv1.NodeRoleLabelPrefix + "/anyRole": "",
433+
434+
clusterv1.ManagedNodeLabelDomain: "",
435+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain: "",
436+
clusterv1.ManagedNodeLabelDomain + "/anything": "",
437+
"custom-prefix." + clusterv1.ManagedNodeLabelDomain + "/anything": "",
438+
439+
clusterv1.NodeRestrictionLabelDomain: "",
440+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain: "",
441+
clusterv1.NodeRestrictionLabelDomain + "/anything": "",
442+
"custom-prefix." + clusterv1.NodeRestrictionLabelDomain + "/anything": "",
443+
}
238444

239445
// Append arbitrary labels.
240446
allLabels := map[string]string{

0 commit comments

Comments
 (0)