Skip to content

Commit

Permalink
unit-tests: modify the controller tests for HCP
Browse files Browse the repository at this point in the history
Put the kubeletconfig controller tests inside a DescribeTable
where one entry is for OCP and another for HCP.

Signed-off-by: Talor Itzhak <[email protected]>
  • Loading branch information
Tal-or committed Sep 17, 2024
1 parent 2f256ea commit 330b608
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 23 deletions.
87 changes: 64 additions & 23 deletions controllers/kubeletconfig_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/k8stopologyawareschedwg/deployer/pkg/deployer/platform"
nropv1 "github.com/openshift-kni/numaresources-operator/api/numaresourcesoperator/v1"
"github.com/openshift-kni/numaresources-operator/pkg/objectnames"
rteconfig "github.com/openshift-kni/numaresources-operator/rte/pkg/config"

testobjs "github.com/openshift-kni/numaresources-operator/internal/objects"
)

type reconcilerBuilderFunc func(...runtime.Object) (*KubeletConfigReconciler, error)

const (
bufferSize = 1024
)
Expand All @@ -50,15 +53,31 @@ func NewFakeKubeletConfigReconciler(initObjects ...runtime.Object) (*KubeletConf
Scheme: scheme.Scheme,
Namespace: testNamespace,
Recorder: record.NewFakeRecorder(bufferSize),
Platform: platform.OpenShift,
}, nil
}

func NewFakeKubeletConfigReconcilerForHyperShift(initObjects ...runtime.Object) (*KubeletConfigReconciler, error) {
fakeClient := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithRuntimeObjects(initObjects...).Build()
return &KubeletConfigReconciler{
Client: fakeClient,
Scheme: scheme.Scheme,
Namespace: testNamespace,
Recorder: record.NewFakeRecorder(bufferSize),
Platform: platform.HyperShift,
}, nil

}

var _ = Describe("Test KubeletConfig Reconcile", func() {
Context("with KubeletConfig objects already present in the cluster", func() {
DescribeTableSubtree("On different platforms with KubeletConfig objects already present in the cluster", func(newFakeReconciler reconcilerBuilderFunc, clusterPlatform platform.Platform) {
var nro *nropv1.NUMAResourcesOperator
var mcp1 *machineconfigv1.MachineConfigPool
var mcoKc1 *machineconfigv1.KubeletConfig
var label1 map[string]string
var key client.ObjectKey
var poolName string
cmKc1 := &corev1.ConfigMap{}

BeforeEach(func() {
label1 = map[string]string{
Expand All @@ -70,57 +89,62 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {
})
kubeletConfig := &kubeletconfigv1beta1.KubeletConfiguration{}
mcoKc1 = testobjs.NewKubeletConfig("test1", label1, mcp1.Spec.MachineConfigSelector, kubeletConfig)
key = client.ObjectKeyFromObject(mcoKc1)
poolName = mcp1.Name

if clusterPlatform == platform.HyperShift {
poolName = "test-hostedcluster1"
label1[HyperShiftNodePoolLabel] = poolName
cmKc1 = testobjs.NewKubeletConfigConfigMap("test1", label1, mcoKc1)
key = client.ObjectKeyFromObject(cmKc1)
}
})

Context("on the first iteration", func() {
It("without NRO present, should wait", func() {
reconciler, err := NewFakeKubeletConfigReconciler(mcp1, mcoKc1)
reconciler, err := newFakeReconciler(mcp1, mcoKc1, cmKc1)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(mcoKc1)
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(reconcile.Result{RequeueAfter: kubeletConfigRetryPeriod}))
})
It("with NRO present, should create configmap", func() {
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, mcoKc1)
reconciler, err := newFakeReconciler(nro, mcp1, mcoKc1, cmKc1)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(mcoKc1)
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(reconcile.Result{}))

cm := &corev1.ConfigMap{}
key = client.ObjectKey{
Namespace: testNamespace,
Name: objectnames.GetComponentName(nro.Name, mcp1.Name),
Name: objectnames.GetComponentName(nro.Name, poolName),
}
Expect(reconciler.Client.Get(context.TODO(), key, cm)).ToNot(HaveOccurred())
})
It("with NRO present, the created configmap should have the linking labels", func() {
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, mcoKc1)
reconciler, err := newFakeReconciler(nro, mcp1, mcoKc1, cmKc1)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(mcoKc1)
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(reconcile.Result{}))

cm := &corev1.ConfigMap{}
key = client.ObjectKey{
Namespace: testNamespace,
Name: objectnames.GetComponentName(nro.Name, mcp1.Name),
Name: objectnames.GetComponentName(nro.Name, poolName),
}
Expect(reconciler.Client.Get(context.TODO(), key, cm)).ToNot(HaveOccurred())
Expect(cm.Labels).To(HaveKeyWithValue(rteconfig.LabelOperatorName, nro.Name))
Expect(cm.Labels).To(HaveKeyWithValue(rteconfig.LabelNodeGroupName+"/"+rteconfig.LabelNodeGroupKindMachineConfigPool, mcp1.Name))
Expect(cm.Labels).To(HaveKeyWithValue(rteconfig.LabelNodeGroupName+"/"+rteconfig.LabelNodeGroupKindMachineConfigPool, poolName))
})
It("should send events when NRO present and operation succesfull", func() {
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, mcoKc1)
It("should send events when NRO present and operation successful", func() {
reconciler, err := newFakeReconciler(nro, mcp1, mcoKc1, cmKc1)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(mcoKc1)
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(reconcile.Result{}))
Expand All @@ -134,7 +158,9 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {

It("should send events when NRO present and operation failure", func() {
brokenMcoKc := testobjs.NewKubeletConfigWithData("broken", label1, mcp1.Spec.MachineConfigSelector, []byte(""))
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, brokenMcoKc)
// on HyperShift we can mimic this behavior by not having a ConfigMap with a KubeletConfig
// present on the cluster at all
reconciler, err := newFakeReconciler(nro, mcp1, brokenMcoKc)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(brokenMcoKc)
Expand All @@ -150,7 +176,9 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {

It("should skip invalid kubeletconfig", func() {
invalidMcoKc := testobjs.NewKubeletConfigWithoutData("payloadless", label1, mcp1.Spec.MachineConfigSelector)
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, invalidMcoKc)
// adding a CM for when this test emulates HyperShift platform
invalidCmMcoKc := testobjs.NewKubeletConfigConfigMap("payloadless", label1, invalidMcoKc)
reconciler, err := newFakeReconciler(nro, mcp1, invalidMcoKc, invalidCmMcoKc)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(invalidMcoKc)
Expand All @@ -167,8 +195,9 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {

It("should ignore non-matching kubeketconfigs", func() {
ctrlPlaneKc := testobjs.NewKubeletConfigAutoresizeControlPlane()

reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1, mcoKc1, ctrlPlaneKc)
// adding a CM for when this test emulates HyperShift platform
ctrlPlaneCmKc := testobjs.NewKubeletConfigConfigMap(ctrlPlaneKc.Name, label1, ctrlPlaneKc)
reconciler, err := newFakeReconciler(nro, mcp1, mcoKc1, ctrlPlaneKc, ctrlPlaneCmKc)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(ctrlPlaneKc)
Expand All @@ -185,35 +214,44 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {
})

It("should process matching kubeletconfig, then ignore non-matching kubeketconfig", func() {
reconciler, err := NewFakeKubeletConfigReconciler(nro, mcp1)
reconciler, err := newFakeReconciler(nro, mcp1)
Expect(err).ToNot(HaveOccurred())

fakeRecorder, ok := reconciler.Recorder.(*record.FakeRecorder)
Expect(ok).To(BeTrue())

err = reconciler.Client.Create(context.TODO(), mcoKc1)
var reconciledObj client.Object
reconciledObj = mcoKc1
if clusterPlatform == platform.HyperShift {
reconciledObj = cmKc1
}
err = reconciler.Client.Create(context.TODO(), reconciledObj)
Expect(err).ToNot(HaveOccurred())

key := client.ObjectKeyFromObject(mcoKc1)
result, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(reconcile.Result{}))

cm := &corev1.ConfigMap{}
key = client.ObjectKey{
Namespace: testNamespace,
Name: objectnames.GetComponentName(nro.Name, mcp1.Name),
Name: objectnames.GetComponentName(nro.Name, poolName),
}
Expect(reconciler.Client.Get(context.TODO(), key, cm)).ToNot(HaveOccurred())
// verify creation event
event := <-fakeRecorder.Events
Expect(event).To(ContainSubstring("ProcessOK"))
Expect(event).To(ContainSubstring(mcoKc1.Name))
Expect(event).To(ContainSubstring(reconciledObj.GetName()))

ctrlPlaneKc := testobjs.NewKubeletConfigAutoresizeControlPlane()
err = reconciler.Client.Create(context.TODO(), ctrlPlaneKc)
Expect(err).ToNot(HaveOccurred())

// adding a CM for when this test emulates HyperShift platform
ctrlPlaneCmKc := testobjs.NewKubeletConfigConfigMap(ctrlPlaneKc.Name, label1, ctrlPlaneKc)
err = reconciler.Client.Create(context.TODO(), ctrlPlaneCmKc)
Expect(err).ToNot(HaveOccurred())

key = client.ObjectKeyFromObject(ctrlPlaneKc)
result, err = reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
Expect(err).ToNot(HaveOccurred())
Expand All @@ -225,5 +263,8 @@ var _ = Describe("Test KubeletConfig Reconcile", func() {
Expect(event).To(ContainSubstring(ctrlPlaneKc.Name))
})
})
})
},
Entry("OpenShift Platform", NewFakeKubeletConfigReconciler, platform.OpenShift),
Entry("HyperShift Platform", NewFakeKubeletConfigReconcilerForHyperShift, platform.HyperShift),
)
})
27 changes: 27 additions & 0 deletions internal/objects/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
package objects

import (
"bytes"
"encoding/json"
"time"

machineconfigv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
serializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/client-go/kubernetes/scheme"
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"

nropv1 "github.com/openshift-kni/numaresources-operator/api/numaresourcesoperator/v1"
Expand Down Expand Up @@ -149,6 +152,30 @@ func NewKubeletConfigAutoresizeControlPlane() *machineconfigv1.KubeletConfig {
return ctrlPlaneKc
}

func NewKubeletConfigConfigMap(name string, labels map[string]string, config *machineconfigv1.KubeletConfig) *corev1.ConfigMap {
yamlSerializer := serializer.NewSerializerWithOptions(
serializer.DefaultMetaFactory, scheme.Scheme, scheme.Scheme,
serializer.SerializerOptions{Yaml: true, Pretty: true, Strict: true})

buff := &bytes.Buffer{}
// supervised testing environment
// no need to check for error
_ = yamlSerializer.Encode(config, buff)
return &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: corev1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
},
Data: map[string]string{
"config": buff.String(),
},
}
}

func NewNamespace(name string) *corev1.Namespace {
return &corev1.Namespace{
TypeMeta: metav1.TypeMeta{
Expand Down

0 comments on commit 330b608

Please sign in to comment.