diff --git a/.github/workflows/pipeline.yaml b/.github/workflows/pipeline.yaml index ed5c2ce..177ab0b 100644 --- a/.github/workflows/pipeline.yaml +++ b/.github/workflows/pipeline.yaml @@ -8,13 +8,11 @@ on: tags: - v* paths-ignore: - - '**.md' + - 'docs/**' + - '**/*.md' pull_request: branches: - main - paths-ignore: - - 'docs/**' - - '**/*.md' permissions: contents: read diff --git a/config/lcmi/manager/kustomization.yaml b/config/lcmi/manager/kustomization.yaml index 76a7774..d51ee13 100644 --- a/config/lcmi/manager/kustomization.yaml +++ b/config/lcmi/manager/kustomization.yaml @@ -1,9 +1,3 @@ resources: - manager.yaml - service.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: ironcore-dev/lifecycle-service - newTag: v0.0.1-dev-202403061552 diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index d897eb3..5c5f0b8 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,8 +1,2 @@ resources: - manager.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: ironcore-dev/lifecycle-controller-manager - newTag: v0.0.1-dev-202403061527 diff --git a/internal/controllers/machine_controller.go b/internal/controllers/machine_controller.go index 928f144..c449010 100644 --- a/internal/controllers/machine_controller.go +++ b/internal/controllers/machine_controller.go @@ -102,11 +102,11 @@ func (r *MachineReconciler) reconcileScan( Name: obj.Name, Namespace: obj.Namespace, })) - scanResponse := resp.Msg if err != nil { log.Error(err, "failed to send scan request") return ctrl.Result{}, err } + scanResponse := resp.Msg if LCIMRequestResultToString[scanResponse.Result].IsScheduled() { obj.Status.Message = StatusMessageScanRequestSubmitted return ctrl.Result{}, nil @@ -135,11 +135,11 @@ func (r *MachineReconciler) reconcileInstall( Name: obj.Name, Namespace: obj.Namespace, })) - installResponse := resp.Msg if err != nil { log.Error(err, "failed to send install request") return ctrl.Result{}, err } + installResponse := resp.Msg if LCIMRequestResultToString[installResponse.Result].IsScheduled() { obj.Status.Message = StatusMessageInstallationScheduled return ctrl.Result{}, nil diff --git a/internal/controllers/machine_controller_test.go b/internal/controllers/machine_controller_test.go index 6d48155..c2595f7 100644 --- a/internal/controllers/machine_controller_test.go +++ b/internal/controllers/machine_controller_test.go @@ -11,6 +11,7 @@ import ( "github.com/ironcore-dev/lifecycle-manager/util/testutil/mock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -18,7 +19,6 @@ import ( lifecyclev1alpha1 "github.com/ironcore-dev/lifecycle-manager/api/lifecycle/v1alpha1" commonv1alpha1 "github.com/ironcore-dev/lifecycle-manager/lcmi/api/common/v1alpha1" machinev1alpha1 "github.com/ironcore-dev/lifecycle-manager/lcmi/api/machine/v1alpha1" - "github.com/ironcore-dev/lifecycle-manager/util/apiutil" "github.com/ironcore-dev/lifecycle-manager/util/testutil" "github.com/ironcore-dev/lifecycle-manager/util/uuidutil" ) @@ -39,11 +39,16 @@ var _ = Describe("Machine controller", func() { Context("When Machine object is being deleted", func() { It("Should interrupt reconciliation and return empty result with no error", func() { + now := metav1.Now() + machine := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + WithDeletionTimestamp(&now). + WithFinalizers([]string{"test-suite-finalizer"}). + MachineFromUnstructured().Complete() + Expect(machine).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineObject("sample", "default", - mock.MachineWithDeletionTimestamp(), - mock.MachineWithFinalizer(), - ))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machine)) machineRec := NewMachineReconciler(c, s) req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "sample"}} res, err := machineRec.Reconcile(context.Background(), req) @@ -55,11 +60,14 @@ var _ = Describe("Machine controller", func() { Context("When referred MachineType object is not found", func() { It("Should interrupt reconciliation and return empty result with error", func() { now := metav1.Now() + machine := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + MachineFromUnstructured().WithMachineTypeRef("sample").Complete() + Expect(machine).NotTo(BeNil()) machineKey := types.NamespacedName{Namespace: "default", Name: "sample"} s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineObject("sample", "default", - mock.MachineWithMachineTypeRef("sample"), - ))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machine)) machineRec := NewMachineReconciler(c, s) brokerClient := fake.NewMachineClient(map[string]*machinev1alpha1.MachineStatus{ uuidutil.UUIDFromObjectKey(machineKey): { @@ -73,6 +81,7 @@ var _ = Describe("Machine controller", func() { req := ctrl.Request{NamespacedName: machineKey} res, err := machineRec.Reconcile(context.Background(), req) Expect(err).To(HaveOccurred()) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) Expect(res).To(Equal(ctrl.Result{})) }) }) @@ -80,8 +89,13 @@ var _ = Describe("Machine controller", func() { Context("When new scan request submitted", func() { It("Should update Machine object's status with corresponding message", func() { machineKey := types.NamespacedName{Namespace: "default", Name: "sample"} + machine := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + MachineFromUnstructured().Complete() + Expect(machine).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineObject("sample", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machine)) machineRec := NewMachineReconciler(c, s) brokerClient := fake.NewMachineClient(map[string]*machinev1alpha1.MachineStatus{}) machineRec.MachineServiceClient = brokerClient @@ -89,10 +103,6 @@ var _ = Describe("Machine controller", func() { res, err := machineRec.Reconcile(context.Background(), req) Expect(err).NotTo(HaveOccurred()) Expect(res).To(Equal(ctrl.Result{})) - - broker, _ := machineRec.MachineServiceClient.(*fake.MachineClient) - entry := broker.ReadCache(uuidutil.UUIDFromObjectKey(machineKey)) - Expect(entry).NotTo(BeNil()) reconciledMachine := &lifecyclev1alpha1.Machine{} err = machineRec.Get(context.Background(), machineKey, reconciledMachine) Expect(err).NotTo(HaveOccurred()) @@ -103,8 +113,13 @@ var _ = Describe("Machine controller", func() { Context("When failed to send scan request", func() { It("Should interrupt reconciliation and return empty result with error", func() { machineKey := types.NamespacedName{Namespace: "default", Name: "failed-scan"} + machine := mock.NewUnstructuredBuilder(). + WithName("failed-scan"). + WithNamespace("default"). + MachineFromUnstructured().Complete() + Expect(machine).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineObject("failed-scan", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machine)) machineRec := NewMachineReconciler(c, s) brokerClient := fake.NewMachineClient(map[string]*machinev1alpha1.MachineStatus{}) machineRec.MachineServiceClient = brokerClient @@ -115,56 +130,25 @@ var _ = Describe("Machine controller", func() { }) }) - Context("When installed packages match desired state", func() { - It("Should update Machine object's status with scan timestamp and result", func() { - now := metav1.Now() - expectedPackages := []*commonv1alpha1.PackageVersion{{Name: "bios", Version: "1.0.0"}} - machine := mock.NewMachineObject("sample", "default", - mock.MachineWithMachineTypeRef("sample"), - mock.MachineWithLabels(map[string]string{"env": "test"}), - mock.MachineStatusWithInstalledPackages(apiutil.PackageVersionsToKubeAPI(expectedPackages))) - machineType := mock.NewMachineTypeObject("sample", "default", - mock.MachineTypeWithGroup(lifecyclev1alpha1.MachineGroup{ - MachineSelector: metav1.LabelSelector{MatchLabels: map[string]string{"env": "test"}}, - Packages: apiutil.PackageVersionsToKubeAPI(expectedPackages)})) - - machineKey := types.NamespacedName{Namespace: "default", Name: "sample"} - s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, - testutil.WithRuntimeObject(machine), - testutil.WithRuntimeObject(machineType)) - machineRec := NewMachineReconciler(c, s) - brokerClient := fake.NewMachineClient(map[string]*machinev1alpha1.MachineStatus{ - uuidutil.UUIDFromObjectKey(machineKey): { - LastScanTime: convertutil.TimeToTimestampPtr(now), - LastScanResult: 1, - InstalledPackages: expectedPackages, - }}) - machineRec.MachineServiceClient = brokerClient - req := ctrl.Request{NamespacedName: machineKey} - res, err := machineRec.Reconcile(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(res).To(Equal(ctrl.Result{})) - - reconciledMachine := &lifecyclev1alpha1.Machine{} - err = machineRec.Get(context.Background(), machineKey, reconciledMachine) - Expect(err).NotTo(HaveOccurred()) - Expect(reconciledMachine.Status.InstalledPackages).To(Equal(apiutil.PackageVersionsToKubeAPI(expectedPackages))) - }) - }) - Context("When packages installation scheduled", func() { It("Should update Machine object's status with corresponding message", func() { now := metav1.Now() desiredPackages := []lifecyclev1alpha1.PackageVersion{{Name: "bios", Version: "1.0.0"}} - machine := mock.NewMachineObject("sample", "default", - mock.MachineWithMachineTypeRef("sample"), - mock.MachineWithLabels(map[string]string{"env": "test"})) - machineType := mock.NewMachineTypeObject("sample", "default", - mock.MachineTypeWithGroup(lifecyclev1alpha1.MachineGroup{ + machine := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + WithLabels(map[string]string{"env": "test"}). + MachineFromUnstructured().WithMachineTypeRef("sample").Complete() + Expect(machine).NotTo(BeNil()) + machineType := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + MachineTypeFromUnstructured(). + WithMachineGroups([]lifecyclev1alpha1.MachineGroup{{ MachineSelector: metav1.LabelSelector{MatchLabels: map[string]string{"env": "test"}}, - Packages: desiredPackages})) - + Packages: desiredPackages}}). + Complete() + Expect(machineType).NotTo(BeNil()) machineKey := types.NamespacedName{Namespace: "default", Name: "sample"} s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) c := testutil.SetupClient(s, @@ -193,14 +177,19 @@ var _ = Describe("Machine controller", func() { It("Should interrupt reconciliation and return empty result with error", func() { now := metav1.Now() desiredPackages := []lifecyclev1alpha1.PackageVersion{{Name: "bios", Version: "1.0.0"}} - machine := mock.NewMachineObject("failed-install", "default", - mock.MachineWithMachineTypeRef("sample"), - mock.MachineWithLabels(map[string]string{"env": "test"})) - machineType := mock.NewMachineTypeObject("sample", "default", - mock.MachineTypeWithGroup(lifecyclev1alpha1.MachineGroup{ + machine := mock.NewUnstructuredBuilder(). + WithName("failed-install"). + WithNamespace("default"). + WithLabels(map[string]string{"env": "test"}). + MachineFromUnstructured().WithMachineTypeRef("sample").Complete() + Expect(machine).NotTo(BeNil()) + machineType := mock.NewUnstructuredBuilder(). + WithName("sample").WithNamespace("default").MachineTypeFromUnstructured(). + WithMachineGroups([]lifecyclev1alpha1.MachineGroup{{ MachineSelector: metav1.LabelSelector{MatchLabels: map[string]string{"env": "test"}}, - Packages: desiredPackages})) - + Packages: desiredPackages}}). + Complete() + Expect(machineType).NotTo(BeNil()) machineKey := types.NamespacedName{Namespace: "default", Name: "failed-install"} s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) c := testutil.SetupClient(s, diff --git a/internal/controllers/machinetype_controller.go b/internal/controllers/machinetype_controller.go index 36dff44..ea39292 100644 --- a/internal/controllers/machinetype_controller.go +++ b/internal/controllers/machinetype_controller.go @@ -94,11 +94,11 @@ func (r *MachineTypeReconciler) reconcile( Name: obj.Name, Namespace: obj.Namespace, })) - scanResponse := resp.Msg if err != nil { log.Error(err, "failed to send scan request") return ctrl.Result{}, err } + scanResponse := resp.Msg if LCIMRequestResultToString[scanResponse.Result].IsScheduled() { obj.Status.Message = StatusMessageScanRequestSubmitted return ctrl.Result{}, nil diff --git a/internal/controllers/machinetype_controller_test.go b/internal/controllers/machinetype_controller_test.go index 9446007..95ca882 100644 --- a/internal/controllers/machinetype_controller_test.go +++ b/internal/controllers/machinetype_controller_test.go @@ -37,11 +37,16 @@ var _ = Describe("MachineType controller", func() { Context("When MachineType object is being deleted", func() { It("Should interrupt reconciliation and return empty result with no error", func() { + now := metav1.Now() + machineType := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + WithDeletionTimestamp(&now). + WithFinalizers([]string{"test-suite-finalizer"}). + MachineTypeFromUnstructured().Complete() + Expect(machineType).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineTypeObject("sample", "default", - mock.MachineTypeWithDeletionTimestamp(), - mock.MachineTypeWithFinalizer(), - ))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machineType)) machinetypeRec := NewMachineTypeReconciler(c, s) req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "sample"}} res, err := machinetypeRec.Reconcile(context.Background(), req) @@ -53,8 +58,13 @@ var _ = Describe("MachineType controller", func() { Context("When new scan request submitted", func() { It("Should update MachineType object's status with corresponding message", func() { machinetypeKey := types.NamespacedName{Namespace: "default", Name: "sample"} + machineType := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + MachineTypeFromUnstructured().Complete() + Expect(machineType).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineTypeObject("sample", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machineType)) machinetypeRec := NewMachineTypeReconciler(c, s) brokerClient := fake.NewMachineTypeClient(map[string]*machinetypev1alpha1.MachineTypeStatus{}) machinetypeRec.MachineTypeServiceClient = brokerClient @@ -62,9 +72,6 @@ var _ = Describe("MachineType controller", func() { res, err := machinetypeRec.Reconcile(context.Background(), req) Expect(err).NotTo(HaveOccurred()) Expect(res).To(Equal(ctrl.Result{})) - broker, _ := machinetypeRec.MachineTypeServiceClient.(*fake.MachineTypeClient) - entry := broker.ReadCache(uuidutil.UUIDFromObjectKey(machinetypeKey)) - Expect(entry).NotTo(BeNil()) reconciledMachineType := &lifecyclev1alpha1.MachineType{} err = machinetypeRec.Get(context.Background(), machinetypeKey, reconciledMachineType) Expect(err).NotTo(HaveOccurred()) @@ -75,8 +82,13 @@ var _ = Describe("MachineType controller", func() { Context("When failed to send scan request", func() { It("Should interrupt reconciliation and return empty result with error", func() { machinetypeKey := types.NamespacedName{Namespace: "default", Name: "failed-scan"} + machineType := mock.NewUnstructuredBuilder(). + WithName("failed-scan"). + WithNamespace("default"). + MachineTypeFromUnstructured().Complete() + Expect(machineType).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineTypeObject("failed-scan", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machineType)) machinetypeRec := NewMachineTypeReconciler(c, s) brokerClient := fake.NewMachineTypeClient(map[string]*machinetypev1alpha1.MachineTypeStatus{}) machinetypeRec.MachineTypeServiceClient = brokerClient @@ -91,8 +103,13 @@ var _ = Describe("MachineType controller", func() { It("Should update MachineType object's status with scan timestamp and result", func() { now := metav1.Now() machinetypeKey := types.NamespacedName{Namespace: "default", Name: "sample"} + machineType := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + MachineTypeFromUnstructured().Complete() + Expect(machineType).NotTo(BeNil()) s := testutil.SetupScheme(testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewMachineTypeObject("sample", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(machineType)) machinetypeRec := NewMachineTypeReconciler(c, s) brokerClient := fake.NewMachineTypeClient(map[string]*machinetypev1alpha1.MachineTypeStatus{ uuidutil.UUIDFromObjectKey(machinetypeKey): { @@ -108,12 +125,10 @@ var _ = Describe("MachineType controller", func() { res, err := machinetypeRec.Reconcile(context.Background(), req) Expect(err).NotTo(HaveOccurred()) Expect(res).To(Equal(ctrl.Result{})) - broker, _ := machinetypeRec.MachineTypeServiceClient.(*fake.MachineTypeClient) - entry := broker.ReadCache(uuidutil.UUIDFromObjectKey(machinetypeKey)) - Expect(entry).NotTo(BeNil()) reconciledMachineType := &lifecyclev1alpha1.MachineType{} err = machinetypeRec.Get(context.Background(), machinetypeKey, reconciledMachineType) Expect(err).NotTo(HaveOccurred()) + Expect(reconciledMachineType.Status).To(Equal(machineType.Status)) }) }) }) diff --git a/internal/controllers/onboarding_controller.go b/internal/controllers/onboarding_controller.go index b20880f..ad8bbf0 100644 --- a/internal/controllers/onboarding_controller.go +++ b/internal/controllers/onboarding_controller.go @@ -6,6 +6,7 @@ package controllers import ( "context" "fmt" + "strings" "time" "github.com/go-logr/logr" @@ -101,7 +102,7 @@ func (r *OnboardingReconciler) onboardMachineType(ctx context.Context, obj *oobv typeName := obj.Status.SKU[:4] machineType := &v1alpha1.MachineType{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s", manufacturer, typeName), + Name: fmt.Sprintf("%s-%s", strings.ToLower(manufacturer), strings.ToLower(typeName)), Namespace: obj.Namespace, }, Spec: v1alpha1.MachineTypeSpec{ @@ -132,9 +133,11 @@ func (r *OnboardingReconciler) onboardMachine(ctx context.Context, obj *oobv1alp Namespace: obj.Namespace, }, Spec: v1alpha1.MachineSpec{ - MachineTypeRef: corev1.LocalObjectReference{Name: fmt.Sprintf("%s-%s", manufacturer, typeName)}, - OOBMachineRef: corev1.LocalObjectReference{Name: obj.Name}, - ScanPeriod: r.ScanPeriod, + MachineTypeRef: corev1.LocalObjectReference{ + Name: fmt.Sprintf("%s-%s", strings.ToLower(manufacturer), strings.ToLower(typeName)), + }, + OOBMachineRef: corev1.LocalObjectReference{Name: obj.Name}, + ScanPeriod: r.ScanPeriod, }, } if err := r.Create(ctx, machine); err != nil { diff --git a/internal/controllers/onboarding_controller_test.go b/internal/controllers/onboarding_controller_test.go index bc4d669..fd6c109 100644 --- a/internal/controllers/onboarding_controller_test.go +++ b/internal/controllers/onboarding_controller_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -34,8 +35,10 @@ var _ = Describe("Onboarding controller", func() { Context("When OOB object's status is empty", func() { It("Should requeue OOB object's reconciliation", func() { + oob := mock.NewUnstructuredBuilder().WithName("sample").WithNamespace("default"). + OOBFromUnstructured().Complete() s := testutil.SetupScheme(testutil.WithGroupVersion(oobv1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewOOBObject("sample", "default"))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(oob)) onboardingRec := NewOnboardingReconciler(c, s) req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "sample"}} res, err := onboardingRec.Reconcile(context.Background(), req) @@ -46,11 +49,15 @@ var _ = Describe("Onboarding controller", func() { Context("When OOB object is being deleted", func() { It("Should interrupt reconciliation and return empty result with no error", func() { + now := metav1.Now() + oob := mock.NewUnstructuredBuilder(). + WithName("sample"). + WithNamespace("default"). + WithDeletionTimestamp(&now). + WithFinalizers([]string{"test-suite-finalizer"}). + OOBFromUnstructured().Complete() s := testutil.SetupScheme(testutil.WithGroupVersion(oobv1alpha1.AddToScheme)) - c := testutil.SetupClient(s, testutil.WithRuntimeObject(mock.NewOOBObject("sample", "default", - mock.OOBWithDeletionTimestamp(), - mock.OOBWithFinalizer(), - ))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(oob)) onboardingRec := NewOnboardingReconciler(c, s) req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "default", Name: "sample"}} res, err := onboardingRec.Reconcile(context.Background(), req) @@ -61,13 +68,17 @@ var _ = Describe("Onboarding controller", func() { Context("When OOB object is being processed normally", func() { It("Should create MachineType object if absent", func() { - expectedMachineTypeKey := types.NamespacedName{Namespace: "default", Name: "Sample-0000"} + expectedMachineTypeKey := types.NamespacedName{Namespace: "default", Name: "sample-0000"} + oob := mock.NewUnstructuredBuilder().WithName("sample").WithNamespace("default"). + OOBFromUnstructured(). + WithManufacturer("Sample"). + WithSKU("0000X0000"). + Complete() s := testutil.SetupScheme( testutil.WithGroupVersion(oobv1alpha1.AddToScheme), testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme), ) - c := testutil.SetupClient(s, testutil.WithRuntimeObject( - mock.NewOOBObject("sample", "default", mock.OOBWithStatus()))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(oob)) onboardingRec := NewOnboardingReconciler(c, s) onboardedMachineType := &lifecyclev1alpha1.MachineType{} err := onboardingRec.Get(context.Background(), expectedMachineTypeKey, onboardedMachineType) @@ -85,12 +96,16 @@ var _ = Describe("Onboarding controller", func() { Context("When OOB object is being processed normally", func() { It("Should create Machine object if absent", func() { expectedMachineKey := types.NamespacedName{Namespace: "default", Name: "sample"} + oob := mock.NewUnstructuredBuilder().WithName("sample").WithNamespace("default"). + OOBFromUnstructured(). + WithManufacturer("Sample"). + WithSKU("0000X0000"). + Complete() s := testutil.SetupScheme( testutil.WithGroupVersion(oobv1alpha1.AddToScheme), testutil.WithGroupVersion(lifecyclev1alpha1.AddToScheme), ) - c := testutil.SetupClient(s, testutil.WithRuntimeObject( - mock.NewOOBObject("sample", "default", mock.OOBWithStatus()))) + c := testutil.SetupClient(s, testutil.WithRuntimeObject(oob)) onboardingRec := NewOnboardingReconciler(c, s) onboardedMachine := &lifecyclev1alpha1.Machine{} err := onboardingRec.Get(context.Background(), expectedMachineKey, onboardedMachine) diff --git a/util/testutil/fake/machine_client.go b/util/testutil/fake/machine_client.go index c073323..05da158 100644 --- a/util/testutil/fake/machine_client.go +++ b/util/testutil/fake/machine_client.go @@ -46,7 +46,6 @@ func (c *MachineClient) ScanMachine( Result: commonv1alpha1.RequestResult_REQUEST_RESULT_SUCCESS, }), nil } - c.cache[uid] = &machineapiv1alpha1.MachineStatus{} return connect.NewResponse(&machineapiv1alpha1.ScanMachineResponse{ Result: commonv1alpha1.RequestResult_REQUEST_RESULT_SCHEDULED, }), nil diff --git a/util/testutil/fake/machinetype_client.go b/util/testutil/fake/machinetype_client.go index a3ff1c6..b813c36 100644 --- a/util/testutil/fake/machinetype_client.go +++ b/util/testutil/fake/machinetype_client.go @@ -38,7 +38,7 @@ func (c *MachineTypeClient) ListMachineTypes( } func (c *MachineTypeClient) Scan( - ctx context.Context, + _ context.Context, req *connect.Request[machinetypeapiv1alpha1.ScanRequest], ) (*connect.Response[machinetypeapiv1alpha1.ScanResponse], error) { in := req.Msg @@ -53,7 +53,6 @@ func (c *MachineTypeClient) Scan( Result: commonv1alpha1.RequestResult_REQUEST_RESULT_SUCCESS, }), nil } - c.cache[uid] = &machinetypeapiv1alpha1.MachineTypeStatus{} return connect.NewResponse(&machinetypeapiv1alpha1.ScanResponse{ Result: commonv1alpha1.RequestResult_REQUEST_RESULT_SCHEDULED, }), nil diff --git a/util/testutil/mock/mock_machine.go b/util/testutil/mock/mock_machine.go index a981331..26955f4 100644 --- a/util/testutil/mock/mock_machine.go +++ b/util/testutil/mock/mock_machine.go @@ -4,59 +4,38 @@ package mock import ( - "time" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" lifecyclev1alpha1 "github.com/ironcore-dev/lifecycle-manager/api/lifecycle/v1alpha1" ) -type MachineOption func(*lifecyclev1alpha1.Machine) - -func MachineWithDeletionTimestamp() MachineOption { - return func(o *lifecyclev1alpha1.Machine) { - o.DeletionTimestamp = &metav1.Time{Time: time.Now()} - } +type MachineMockBuilder struct { + inner *lifecyclev1alpha1.Machine } -func MachineWithFinalizer() MachineOption { - return func(o *lifecyclev1alpha1.Machine) { - o.Finalizers = []string{"test-suite-finalizer"} - } +func (b *UnstructuredBuilder) MachineFromUnstructured() *MachineMockBuilder { + var m lifecyclev1alpha1.Machine + b.inner.SetAPIVersion("lifecycle.ironcore.dev/v1alpha1") + b.inner.SetKind("Machine") + _ = runtime.DefaultUnstructuredConverter.FromUnstructured(b.inner.Object, &m) + return &MachineMockBuilder{inner: &m} } -func MachineWithLabels(lbl map[string]string) MachineOption { - return func(o *lifecyclev1alpha1.Machine) { - o.Labels = lbl - } +func (b *MachineMockBuilder) WithMachineTypeRef(name string) *MachineMockBuilder { + b.inner.Spec.MachineTypeRef.Name = name + return b } -func MachineWithMachineTypeRef(name string) MachineOption { - return func(o *lifecyclev1alpha1.Machine) { - o.Spec.MachineTypeRef = corev1.LocalObjectReference{Name: name} - } +func (b *MachineMockBuilder) WithOOBMachineRef(name string) *MachineMockBuilder { + b.inner.Spec.OOBMachineRef.Name = name + return b } -func MachineStatusWithInstalledPackages(pkg []lifecyclev1alpha1.PackageVersion) MachineOption { - return func(o *lifecyclev1alpha1.Machine) { - o.Status.InstalledPackages = pkg - } +func (b *MachineMockBuilder) WithInstalledPackages(pkg []lifecyclev1alpha1.PackageVersion) *MachineMockBuilder { + b.inner.Status.InstalledPackages = pkg + return b } -func NewMachineObject(name, namespace string, opts ...MachineOption) *lifecyclev1alpha1.Machine { - o := &lifecyclev1alpha1.Machine{ - TypeMeta: metav1.TypeMeta{ - Kind: "Machine", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - } - for _, opt := range opts { - opt(o) - } - return o +func (b *MachineMockBuilder) Complete() *lifecyclev1alpha1.Machine { + return b.inner } diff --git a/util/testutil/mock/mock_machinetype.go b/util/testutil/mock/mock_machinetype.go index f03e14b..0038181 100644 --- a/util/testutil/mock/mock_machinetype.go +++ b/util/testutil/mock/mock_machinetype.go @@ -4,46 +4,28 @@ package mock import ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" lifecyclev1alpha1 "github.com/ironcore-dev/lifecycle-manager/api/lifecycle/v1alpha1" ) -type MachineTypeOption func(*lifecyclev1alpha1.MachineType) - -func MachineTypeWithDeletionTimestamp() MachineTypeOption { - return func(o *lifecyclev1alpha1.MachineType) { - o.DeletionTimestamp = &metav1.Time{Time: time.Now()} - } +type MachineTypeMockBuilder struct { + inner *lifecyclev1alpha1.MachineType } -func MachineTypeWithFinalizer() MachineTypeOption { - return func(o *lifecyclev1alpha1.MachineType) { - o.Finalizers = []string{"test-suite-finalizer"} - } +func (b *UnstructuredBuilder) MachineTypeFromUnstructured() *MachineTypeMockBuilder { + var m lifecyclev1alpha1.MachineType + b.inner.SetAPIVersion("lifecycle.ironcore.dev/v1alpha1") + b.inner.SetKind("MachineType") + _ = runtime.DefaultUnstructuredConverter.FromUnstructured(b.inner.Object, &m) + return &MachineTypeMockBuilder{inner: &m} } -func MachineTypeWithGroup(group lifecyclev1alpha1.MachineGroup) MachineTypeOption { - return func(o *lifecyclev1alpha1.MachineType) { - o.Spec.MachineGroups = append(o.Spec.MachineGroups, group) - } +func (b *MachineTypeMockBuilder) WithMachineGroups(groups []lifecyclev1alpha1.MachineGroup) *MachineTypeMockBuilder { + b.inner.Spec.MachineGroups = groups + return b } -func NewMachineTypeObject(name, namespace string, opts ...MachineTypeOption) *lifecyclev1alpha1.MachineType { - o := &lifecyclev1alpha1.MachineType{ - TypeMeta: metav1.TypeMeta{ - Kind: "MachineType", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - } - for _, opt := range opts { - opt(o) - } - return o +func (b *MachineTypeMockBuilder) Complete() *lifecyclev1alpha1.MachineType { + return b.inner } diff --git a/util/testutil/mock/mock_oob.go b/util/testutil/mock/mock_oob.go index 50ef5b7..8c35b4a 100644 --- a/util/testutil/mock/mock_oob.go +++ b/util/testutil/mock/mock_oob.go @@ -4,46 +4,32 @@ package mock import ( - "time" - oobv1alpha1 "github.com/ironcore-dev/oob/api/v1alpha1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) -type OOBObjectOption func(*oobv1alpha1.OOB) +type OOBMockBuilder struct { + inner *oobv1alpha1.OOB +} -func OOBWithDeletionTimestamp() OOBObjectOption { - return func(o *oobv1alpha1.OOB) { - o.DeletionTimestamp = &metav1.Time{Time: time.Now()} - } +func (b *UnstructuredBuilder) OOBFromUnstructured() *OOBMockBuilder { + var o oobv1alpha1.OOB + b.inner.SetAPIVersion("ironcore.dev/v1alpha1") + b.inner.SetKind("OOB") + _ = runtime.DefaultUnstructuredConverter.FromUnstructured(b.inner.Object, &o) + return &OOBMockBuilder{inner: &o} } -func OOBWithFinalizer() OOBObjectOption { - return func(o *oobv1alpha1.OOB) { - o.Finalizers = []string{"test-suite-finalizer"} - } +func (b *OOBMockBuilder) WithManufacturer(manufacturer string) *OOBMockBuilder { + b.inner.Status.Manufacturer = manufacturer + return b } -func OOBWithStatus() OOBObjectOption { - return func(o *oobv1alpha1.OOB) { - o.Status.Manufacturer = "Sample" - o.Status.SKU = "0000X0000" - } +func (b *OOBMockBuilder) WithSKU(sku string) *OOBMockBuilder { + b.inner.Status.SKU = sku + return b } -func NewOOBObject(name, namespace string, opts ...OOBObjectOption) *oobv1alpha1.OOB { - o := &oobv1alpha1.OOB{ - TypeMeta: metav1.TypeMeta{ - Kind: "OOB", - APIVersion: "v1alpha1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - } - for _, opt := range opts { - opt(o) - } - return o +func (b *OOBMockBuilder) Complete() *oobv1alpha1.OOB { + return b.inner } diff --git a/util/testutil/mock/mocker.go b/util/testutil/mock/mocker.go new file mode 100644 index 0000000..93e4bdb --- /dev/null +++ b/util/testutil/mock/mocker.go @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package mock + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +type UnstructuredBuilder struct { + inner unstructured.Unstructured +} + +func NewUnstructuredBuilder() *UnstructuredBuilder { + return &UnstructuredBuilder{inner: unstructured.Unstructured{}} +} + +func (b *UnstructuredBuilder) WithName(name string) *UnstructuredBuilder { + b.inner.SetName(name) + return b +} + +func (b *UnstructuredBuilder) WithNamespace(namespace string) *UnstructuredBuilder { + b.inner.SetNamespace(namespace) + return b +} + +func (b *UnstructuredBuilder) WithDeletionTimestamp(timestamp *metav1.Time) *UnstructuredBuilder { + b.inner.SetDeletionTimestamp(timestamp) + return b +} + +func (b *UnstructuredBuilder) WithLabels(labels map[string]string) *UnstructuredBuilder { + b.inner.SetLabels(labels) + return b +} + +func (b *UnstructuredBuilder) WithFinalizers(finalizers []string) *UnstructuredBuilder { + b.inner.SetFinalizers(finalizers) + return b +}