diff --git a/api/v1alpha1/osbuildenvconfig_types.go b/api/v1alpha1/osbuildenvconfig_types.go index d560b4d4..274a48e7 100644 --- a/api/v1alpha1/osbuildenvconfig_types.go +++ b/api/v1alpha1/osbuildenvconfig_types.go @@ -173,8 +173,8 @@ type ContainerRegistryServiceConfig struct { // OSBuildEnvConfigStatus defines the observed state of OSBuildEnvConfig type OSBuildEnvConfigStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file + // The conditions present the latest available observations of osbuild environment's current state + Conditions []Condition `json:"conditions,omitempty"` } //+kubebuilder:object:root=true diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9bdfa265..40ff6adc 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -601,7 +601,7 @@ func (in *OSBuildEnvConfig) DeepCopyInto(out *OSBuildEnvConfig) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OSBuildEnvConfig. @@ -687,6 +687,13 @@ func (in *OSBuildEnvConfigSpec) DeepCopy() *OSBuildEnvConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OSBuildEnvConfigStatus) DeepCopyInto(out *OSBuildEnvConfigStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OSBuildEnvConfigStatus. diff --git a/config/crd/bases/osbuilder.project-flotta.io_osbuildenvconfigs.yaml b/config/crd/bases/osbuilder.project-flotta.io_osbuildenvconfigs.yaml index df3612c3..32b1ddea 100644 --- a/config/crd/bases/osbuilder.project-flotta.io_osbuildenvconfigs.yaml +++ b/config/crd/bases/osbuilder.project-flotta.io_osbuildenvconfigs.yaml @@ -439,6 +439,32 @@ spec: type: object status: description: OSBuildEnvConfigStatus defines the observed state of OSBuildEnvConfig + properties: + conditions: + description: The conditions present the latest available observations + of osbuild environment's current state + items: + properties: + lastTransitionTime: + description: The last time the condition transit from one status + to another + format: date-time + type: string + message: + description: A human-readable message indicating details about + last transition + type: string + status: + description: Status of the condition, one of True, False, Unknown + type: string + type: + description: Type of status + type: string + required: + - status + - type + type: object + type: array type: object type: object served: true diff --git a/controllers/osbuildenvconfig_controller.go b/controllers/osbuildenvconfig_controller.go index bed12cfd..364176c0 100644 --- a/controllers/osbuildenvconfig_controller.go +++ b/controllers/osbuildenvconfig_controller.go @@ -32,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" @@ -46,6 +47,7 @@ import ( osbuildv1alpha1 "github.com/project-flotta/osbuild-operator/api/v1alpha1" "github.com/project-flotta/osbuild-operator/internal/conf" + "github.com/project-flotta/osbuild-operator/internal/predicates" "github.com/project-flotta/osbuild-operator/internal/repository/certificate" "github.com/project-flotta/osbuild-operator/internal/repository/configmap" "github.com/project-flotta/osbuild-operator/internal/repository/deployment" @@ -362,6 +364,7 @@ func (r *OSBuildEnvConfigReconciler) Reconcile(ctx context.Context, req ctrl.Req func (r *OSBuildEnvConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&osbuildv1alpha1.OSBuildEnvConfig{}). + Owns(&batchv1.Job{}, builder.WithPredicates(predicates.OSBuildEnvConfigJobFinished{})). Complete(r) } @@ -371,7 +374,15 @@ func (r *OSBuildEnvConfigReconciler) Update(ctx context.Context, reqLogger logr. "UID", instance.UID, ) - created, err := r.addFinalizer(ctx, instance) + created, err := r.initConditions(ctx, instance) + if err != nil { + return ctrl.Result{Requeue: true}, nil + } else if created { + reqLogger.Info("Conditions initialized") + return resultQuickRequeue, nil + } + + created, err = r.addFinalizer(ctx, instance) if err != nil { return ctrl.Result{Requeue: true}, nil } else if created { @@ -408,6 +419,23 @@ func (r *OSBuildEnvConfigReconciler) Update(ctx context.Context, reqLogger logr. return resultQuickRequeue, nil } + configured, err := r.ensureWorkersConfigured(ctx, reqLogger, instance) + if err != nil { + return ctrl.Result{Requeue: true}, nil + } else if !configured { + reqLogger.Info("The workers setup jobs are still running") + return ctrl.Result{}, nil + } + + reqLogger.Info("The worker setup jobs are done") + updated, err := r.updateConditions(ctx, reqLogger, instance, osbuildv1alpha1.ConditionReady) + if err != nil { + return ctrl.Result{Requeue: true}, nil + } else if updated { + reqLogger.Info("Updated status conditions") + return resultQuickRequeue, nil + } + return ctrl.Result{}, nil } @@ -542,6 +570,37 @@ func (r *OSBuildEnvConfigReconciler) ensureWorkersExists(ctx context.Context, re return false, nil } +func (r *OSBuildEnvConfigReconciler) ensureWorkersConfigured(ctx context.Context, reqLogger logr.Logger, instance *osbuildv1alpha1.OSBuildEnvConfig) (bool, error) { + for i := range instance.Spec.Workers { + configured, err := r.ensureWorkerConfigured(ctx, reqLogger, instance, &instance.Spec.Workers[i]) + if err != nil { + return false, err + } else if !configured { + return false, nil + } + } + return true, nil +} + +func (r *OSBuildEnvConfigReconciler) ensureWorkerConfigured(ctx context.Context, reqLogger logr.Logger, instance *osbuildv1alpha1.OSBuildEnvConfig, worker *osbuildv1alpha1.WorkerConfig) (bool, error) { + job, err := r.JobRepository.Read(ctx, fmt.Sprintf(workerSetupJobNameFormat, worker.Name), conf.GlobalConf.WorkingNamespace) + if err != nil { + return false, err + } + + if job.Status.Active > 0 { + return false, nil + } + + for _, condition := range job.Status.Conditions { + if condition.Type == batchv1.JobComplete && condition.Status == corev1.ConditionTrue { + return true, nil + } + } + + return false, nil +} + func (r *OSBuildEnvConfigReconciler) ensureWorkerExists(ctx context.Context, reqLogger logr.Logger, instance *osbuildv1alpha1.OSBuildEnvConfig, worker *osbuildv1alpha1.WorkerConfig, composerWorkerAPIRouteHost string) (bool, error) { var workerAddress string var workerUser string @@ -1282,3 +1341,53 @@ func (r *OSBuildEnvConfigReconciler) removeFinalizer(ctx context.Context, reqLog controllerutil.RemoveFinalizer(instance, osBuildOperatorFinalizer) return r.OSBuildEnvConfigRepository.Patch(ctx, oldInstance, instance) } + +func (r *OSBuildEnvConfigReconciler) initConditions(ctx context.Context, instance *osbuildv1alpha1.OSBuildEnvConfig) (bool, error) { + if len(instance.Status.Conditions) > 0 { + return false, nil + } + + oldInstance := instance.DeepCopy() + instance.Status.Conditions = append( + instance.Status.Conditions, + osbuildv1alpha1.Condition{ + Type: osbuildv1alpha1.ConditionInProgress, + Status: metav1.ConditionTrue, + LastTransitionTime: &metav1.Time{Time: time.Now()}, + }, + osbuildv1alpha1.Condition{ + Type: osbuildv1alpha1.ConditionFailed, + Status: metav1.ConditionFalse, + }, + osbuildv1alpha1.Condition{ + Type: osbuildv1alpha1.ConditionReady, + Status: metav1.ConditionFalse, + }, + ) + return true, r.OSBuildEnvConfigRepository.PatchStatus(ctx, oldInstance, instance) +} + +func (r *OSBuildEnvConfigReconciler) updateConditions(ctx context.Context, reqLogger logr.Logger, instance *osbuildv1alpha1.OSBuildEnvConfig, conditionType osbuildv1alpha1.ConditionType) (bool, error) { + oldInstance := instance.DeepCopy() + + updated := false + timeNow := &metav1.Time{Time: time.Now()} + for i, c := range instance.Status.Conditions { + if c.Type == conditionType { + instance.Status.Conditions[i].Status = metav1.ConditionTrue + } else { + instance.Status.Conditions[i].Status = metav1.ConditionFalse + } + + if instance.Status.Conditions[i].Status != oldInstance.Status.Conditions[i].Status { + instance.Status.Conditions[i].LastTransitionTime = timeNow + updated = true + } + } + + if !updated { + return false, nil + } + + return true, r.OSBuildEnvConfigRepository.PatchStatus(ctx, oldInstance, instance) +} diff --git a/controllers/osbuildenvconfig_controller_test.go b/controllers/osbuildenvconfig_controller_test.go index 151b7207..e8742a34 100644 --- a/controllers/osbuildenvconfig_controller_test.go +++ b/controllers/osbuildenvconfig_controller_test.go @@ -329,11 +329,28 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) Context("Handle update", func() { + var ( + conditionsInProgress = []osbuildv1alpha1.Condition{ + { + Type: osbuildv1alpha1.ConditionInProgress, + Status: metav1.ConditionTrue, + }, + { + Type: osbuildv1alpha1.ConditionFailed, + Status: metav1.ConditionFalse, + }, + { + Type: osbuildv1alpha1.ConditionReady, + Status: metav1.ConditionFalse, + }, + } + ) + BeforeEach(func() { osBuildEnvConfigRepository.EXPECT().Read(requestContext, instanceName).Return(&instance, nil) }) - Context("Adding Finalizers", func() { + Context("Initializing the conditions", func() { var ( originalInstance *osbuildv1alpha1.OSBuildEnvConfig ) @@ -342,9 +359,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { originalInstance = instance.DeepCopy() }) - It("Should requeue if it failed to add the finalizer", func() { + It("Should return requeue if it failed to initialize the conditions array", func() { // given - osBuildEnvConfigRepository.EXPECT().Patch(requestContext, originalInstance, &instance).Return(errFailed) + osBuildEnvConfigRepository.EXPECT().PatchStatus(requestContext, originalInstance, &instance).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -352,69 +369,35 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the finalizer was added", func() { + It("Should return requeue if the conditions array was initialized", func() { // given - osBuildEnvConfigRepository.EXPECT().Patch(requestContext, originalInstance, &instance).Return(nil) + osBuildEnvConfigRepository.EXPECT().PatchStatus(requestContext, originalInstance, &instance).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) Expect(result).To(Equal(resultQuickRequeue)) + validateOSBuildEnvConfigConditionArr(instance.Status.Conditions, osbuildv1alpha1.ConditionInProgress) }) }) - Context("With finalizer", func() { - const ( - composerWorkerAPIRouteName = "osbuild-worker" - composerWorkerAPIServiceName = "osbuild-worker" - composerWorkerAPIRouteHost = "osbuild-worker.apps.my-cluster.example.com" - ) - - var ( - composerWorkerAPIRoute routev1.Route - ) - + Context("Conditions array initialized", func() { BeforeEach(func() { - instance.ObjectMeta.Finalizers = []string{osBuildOperatorFinalizer} - - composerWorkerAPIRoute = routev1.Route{ - ObjectMeta: metav1.ObjectMeta{ - Name: composerWorkerAPIRouteName, - Namespace: conf.GlobalConf.WorkingNamespace, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: routev1.RouteSpec{ - To: routev1.RouteTargetReference{ - Kind: "Service", - Name: composerWorkerAPIServiceName, - }, - TLS: &routev1.TLSConfig{ - Termination: routev1.TLSTerminationPassthrough, - }, - }, - } - - }) - - It("Should requeue if failed to get the Route for the Composer Worker API ", func() { - // given - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errFailed) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) + instance.Status.Conditions = append([]osbuildv1alpha1.Condition{}, conditionsInProgress...) }) - Context("Route for the Composer Worker API not found", func() { + Context("Adding Finalizers", func() { + var ( + originalInstance *osbuildv1alpha1.OSBuildEnvConfig + ) BeforeEach(func() { - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errNotFound) + originalInstance = instance.DeepCopy() }) - It("Should requeue if failed to create the Route for the Composer Worker API", func() { + It("Should requeue if it failed to add the finalizer", func() { // given - routeRepository.EXPECT().Create(requestContext, &composerWorkerAPIRoute).Return(errFailed) + osBuildEnvConfigRepository.EXPECT().Patch(requestContext, originalInstance, &instance).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -422,9 +405,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the Route for the Composer Worker API", func() { + It("Should return requeue if the finalizer was added", func() { // given - routeRepository.EXPECT().Create(requestContext, &composerWorkerAPIRoute).Return(nil) + osBuildEnvConfigRepository.EXPECT().Patch(requestContext, originalInstance, &instance).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -433,92 +416,58 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("Route for the Composer Worker API exists ", func() { + Context("With finalizer", func() { + const ( + composerWorkerAPIRouteName = "osbuild-worker" + composerWorkerAPIServiceName = "osbuild-worker" + composerWorkerAPIRouteHost = "osbuild-worker.apps.my-cluster.example.com" + ) + + var ( + composerWorkerAPIRoute routev1.Route + ) + BeforeEach(func() { - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) - composerWorkerAPIRoute.Status.Ingress = []routev1.RouteIngress{ - { - Conditions: []routev1.RouteIngressCondition{ - { - Type: routev1.RouteAdmitted, - }, + instance.ObjectMeta.Finalizers = []string{osBuildOperatorFinalizer} + + composerWorkerAPIRoute = routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: composerWorkerAPIRouteName, + Namespace: conf.GlobalConf.WorkingNamespace, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Spec: routev1.RouteSpec{ + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: composerWorkerAPIServiceName, + }, + TLS: &routev1.TLSConfig{ + Termination: routev1.TLSTerminationPassthrough, }, }, } - }) - - It("Should requeue if failed to check if the Route for the Composer Worker API is ready", func() { - //given - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errFailed) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the Route for the Composer Worker API is not ready", func() { + It("Should requeue if failed to get the Route for the Composer Worker API ", func() { // given - composerWorkerAPIRoute.Status.Ingress[0].Conditions[0].Status = corev1.ConditionFalse - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) - Expect(result).To(Equal(reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10})) + Expect(result).To(Equal(resultRequeue)) }) - Context("Worker API Route is ready", func() { - const ( - certificateDuration = 87600 - composerComposerAPIServiceName = "osbuild-composer" - ) - var ( - composerCertificate certmanagerv1.Certificate - ) + Context("Route for the Composer Worker API not found", func() { BeforeEach(func() { - composerWorkerAPIRoute.Status.Ingress[0].Conditions[0].Status = corev1.ConditionTrue - composerWorkerAPIRoute.Status.Ingress[0].Host = composerWorkerAPIRouteHost - routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) - - composerCertificate = certmanagerv1.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: composerCertificateName, - Namespace: operatorNamespace, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: certmanagerv1.CertificateSpec{ - SecretName: composerCertificateName, - PrivateKey: &certmanagerv1.CertificatePrivateKey{ - Algorithm: "ECDSA", - Size: 256, - }, - DNSNames: []string{ - composerComposerAPIServiceName, - composerWorkerAPIServiceName, - composerWorkerAPIRouteHost, - }, - Duration: &metav1.Duration{ - Duration: time.Hour * certificateDuration, - }, - IssuerRef: certmanagermetav1.ObjectReference{ - Group: "cert-manager.io", - Kind: "Issuer", - Name: caIssuerName, - }, - }, - } - }) - - AfterEach(func() { - instance.ObjectMeta.Finalizers = nil + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if it failed to get the certificate", func() { + It("Should requeue if failed to create the Route for the Composer Worker API", func() { // given - certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errFailed) + routeRepository.EXPECT().Create(requestContext, &composerWorkerAPIRoute).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -526,47 +475,103 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should requeue if failed to create the certificate", func() { + It("Should return requeue if succeeded to create the Route for the Composer Worker API", func() { // given - certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errNotFound) - certificateRepository.EXPECT().Create(requestContext, &composerCertificate).Return(errFailed) + routeRepository.EXPECT().Create(requestContext, &composerWorkerAPIRoute).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + }) + }) + + Context("Route for the Composer Worker API exists ", func() { + BeforeEach(func() { + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) + composerWorkerAPIRoute.Status.Ingress = []routev1.RouteIngress{ + { + Conditions: []routev1.RouteIngressCondition{ + { + Type: routev1.RouteAdmitted, + }, + }, + }, + } + }) + + It("Should requeue if failed to check if the Route for the Composer Worker API is ready", func() { + //given + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) Expect(result).To(Equal(resultRequeue)) + }) - It("Should return requeue if the certificate was created", func() { + It("Should return requeue if the Route for the Composer Worker API is not ready", func() { // given - certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errNotFound) - certificateRepository.EXPECT().Create(requestContext, &composerCertificate).Return(nil) + composerWorkerAPIRoute.Status.Ingress[0].Conditions[0].Status = corev1.ConditionFalse + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) + Expect(result).To(Equal(reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10})) }) - Context("Composer Certificate is already created", func() { + Context("Worker API Route is ready", func() { + const ( + certificateDuration = 87600 + composerComposerAPIServiceName = "osbuild-composer" + ) var ( - composerCertificateSecret corev1.Secret + composerCertificate certmanagerv1.Certificate ) BeforeEach(func() { - certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificate, nil) + composerWorkerAPIRoute.Status.Ingress[0].Conditions[0].Status = corev1.ConditionTrue + composerWorkerAPIRoute.Status.Ingress[0].Host = composerWorkerAPIRouteHost + routeRepository.EXPECT().Read(requestContext, composerWorkerAPIRouteName, operatorNamespace).Return(&composerWorkerAPIRoute, nil) - composerCertificateSecret = corev1.Secret{ + composerCertificate = certmanagerv1.Certificate{ ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: composerCertificateName, + Name: composerCertificateName, + Namespace: operatorNamespace, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Spec: certmanagerv1.CertificateSpec{ + SecretName: composerCertificateName, + PrivateKey: &certmanagerv1.CertificatePrivateKey{ + Algorithm: "ECDSA", + Size: 256, + }, + DNSNames: []string{ + composerComposerAPIServiceName, + composerWorkerAPIServiceName, + composerWorkerAPIRouteHost, + }, + Duration: &metav1.Duration{ + Duration: time.Hour * certificateDuration, + }, + IssuerRef: certmanagermetav1.ObjectReference{ + Group: "cert-manager.io", + Kind: "Issuer", + Name: caIssuerName, + }, }, } }) - It("Should requeue if it failed to get the composer certificate secret", func() { + AfterEach(func() { + instance.ObjectMeta.Finalizers = nil + }) + + It("Should requeue if it failed to get the certificate", func() { // given - secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errFailed) + certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -574,23 +579,47 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Successfully get the composer certificate secret", func() { + It("Should requeue if failed to create the certificate", func() { + // given + certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errNotFound) + certificateRepository.EXPECT().Create(requestContext, &composerCertificate).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) + + It("Should return requeue if the certificate was created", func() { + // given + certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errNotFound) + certificateRepository.EXPECT().Create(requestContext, &composerCertificate).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + }) + + Context("Composer Certificate is already created", func() { var ( - originalComposerCertificateSecret *corev1.Secret - updatedComposerCertificateSecret *corev1.Secret + composerCertificateSecret corev1.Secret ) BeforeEach(func() { - secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificateSecret, nil) - - originalComposerCertificateSecret = composerCertificateSecret.DeepCopy() - updatedComposerCertificateSecret = composerCertificateSecret.DeepCopy() - updatedComposerCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} + certificateRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificate, nil) + + composerCertificateSecret = corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: composerCertificateName, + }, + } }) - It("Should requeue if failed to update the secret owner", func() { + It("Should requeue if it failed to get the composer certificate secret", func() { // given - secretRepository.EXPECT().Patch(requestContext, originalComposerCertificateSecret, updatedComposerCertificateSecret).Return(errFailed) + secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -598,50 +627,23 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to update the secret owner", func() { - // given - secretRepository.EXPECT().Patch(requestContext, originalComposerCertificateSecret, updatedComposerCertificateSecret).Return(nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) - }) - - Context("Composer Certificate Owner is already set", func() { - BeforeEach(func() { - composerCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} - secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificateSecret, nil) - }) - - It("Should requeue if PSQL information is not set", func() { - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) - - }) - - Context("PSQL information is set", func() { - const ( - composerConfigMapName = "osbuild-composer-config" + Context("Successfully get the composer certificate secret", func() { + var ( + originalComposerCertificateSecret *corev1.Secret + updatedComposerCertificateSecret *corev1.Secret ) + BeforeEach(func() { - var sslMode osbuildv1alpha1.DBSSLMode = "disable" - - instance.Spec.Composer.PSQL = &osbuildv1alpha1.ComposerDBConfig{ - ConnectionSecretReference: buildv1.SecretLocalReference{ - Name: dbSecretName, - }, - SSLMode: &sslMode, - } + secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificateSecret, nil) + + originalComposerCertificateSecret = composerCertificateSecret.DeepCopy() + updatedComposerCertificateSecret = composerCertificateSecret.DeepCopy() + updatedComposerCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} }) - It("Should requeue if failed to get the ConfigMap for the osbuild-composer configuration", func() { + It("Should requeue if failed to update the secret owner", func() { // given - configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errFailed) + secretRepository.EXPECT().Patch(requestContext, originalComposerCertificateSecret, updatedComposerCertificateSecret).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -649,48 +651,50 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should requeue if failed to create the ConfigMap for the osbuild-composer configuration", func() { + It("Should return requeue if succeeded to update the secret owner", func() { // given - configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errNotFound) - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + secretRepository.EXPECT().Patch(requestContext, originalComposerCertificateSecret, updatedComposerCertificateSecret).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) + Expect(result).To(Equal(resultQuickRequeue)) + }) + }) + + Context("Composer Certificate Owner is already set", func() { + BeforeEach(func() { + composerCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} + secretRepository.EXPECT().Read(requestContext, composerCertificateName, operatorNamespace).Return(&composerCertificateSecret, nil) }) - It("Should return requeue if the ConfigMap for the osbuild-composer configuration was created", func() { - // given - configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errNotFound) - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + It("Should requeue if PSQL information is not set", func() { // when result, err := reconciler.Reconcile(requestContext, request) // then Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) + Expect(result).To(Equal(resultRequeue)) + }) - Context("ConfigMap for the Composer configuration exists", func() { + Context("PSQL information is set", func() { const ( - composerProxyConfigMapName = "osbuild-composer-proxy-config" + composerConfigMapName = "osbuild-composer-config" ) - var ( - composerConfigConfigMap = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: composerConfigMapName, + BeforeEach(func() { + var sslMode osbuildv1alpha1.DBSSLMode = "disable" + + instance.Spec.Composer.PSQL = &osbuildv1alpha1.ComposerDBConfig{ + ConnectionSecretReference: buildv1.SecretLocalReference{ + Name: dbSecretName, }, + SSLMode: &sslMode, } - ) - - BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(&composerConfigConfigMap, nil) }) - It("Should requeue if failed to get the ConfigMap for the proxy configuration", func() { + It("Should requeue if failed to get the ConfigMap for the osbuild-composer configuration", func() { // given - configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -698,9 +702,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should requeue if failed to create the ConfigMap for the proxy configuration", func() { + It("Should requeue if failed to create the ConfigMap for the osbuild-composer configuration", func() { // given - configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errNotFound) configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) @@ -709,9 +713,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the ConfigMap for the proxy configuration was created", func() { + It("Should return requeue if the ConfigMap for the osbuild-composer configuration was created", func() { // given - configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(nil, errNotFound) configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) @@ -720,26 +724,26 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultQuickRequeue)) }) - Context("ConfigMap for the Proxy configuration exists", func() { + Context("ConfigMap for the Composer configuration exists", func() { const ( - composerDeploymentName = "composer" + composerProxyConfigMapName = "osbuild-composer-proxy-config" ) var ( - proxyConfigConfigMap = corev1.ConfigMap{ + composerConfigConfigMap = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: operatorNamespace, - Name: composerProxyConfigMapName, + Name: composerConfigMapName, }, } ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(&proxyConfigConfigMap, nil) + configMapRepository.EXPECT().Read(requestContext, composerConfigMapName, operatorNamespace).Return(&composerConfigConfigMap, nil) }) - It("Should requeue if failed to get the composer deployment", func() { + It("Should requeue if failed to get the ConfigMap for the proxy configuration", func() { // given - deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -747,78 +751,48 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Composer deployment not found", func() { - BeforeEach(func() { - deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(nil, errNotFound) - }) - - It("Should requeue if failed to create the composer deployment", func() { - // given - deploymentRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) - }) + It("Should requeue if failed to create the ConfigMap for the proxy configuration", func() { + // given + configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) - It("Should return requeue if the composer deployment was created", func() { - // given - deploymentRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) + It("Should return requeue if the ConfigMap for the proxy configuration was created", func() { + // given + configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) }) - Context("Composer Deployment exists", func() { + Context("ConfigMap for the Proxy configuration exists", func() { const ( - composerComposerAPIPortName = "composer-api" + composerDeploymentName = "composer" ) var ( - composerDeployment = appsv1.Deployment{ + proxyConfigConfigMap = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: operatorNamespace, - Name: composerDeploymentName, + Name: composerProxyConfigMapName, }, } - - composerAPIExternalPort = intstr.FromInt(8080) - - composerComposerAPIService corev1.Service ) BeforeEach(func() { - deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(&composerDeployment, nil) - - composerComposerAPIService = corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: composerComposerAPIServiceName, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - Ports: []corev1.ServicePort{ - { - Name: composerComposerAPIPortName, - Port: 443, - Protocol: "TCP", - TargetPort: composerAPIExternalPort, - }, - }, - Selector: map[string]string{ - "app": "osbuild-composer", - }, - }, - } + configMapRepository.EXPECT().Read(requestContext, composerProxyConfigMapName, operatorNamespace).Return(&proxyConfigConfigMap, nil) }) - It("Should requeue if failed to get the composer api service", func() { + It("Should requeue if failed to get the composer deployment", func() { // given - serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(nil, errFailed) + deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -826,14 +800,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Composer API Service not found", func() { + Context("Composer deployment not found", func() { BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(nil, errNotFound) + deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the composer api service", func() { + It("Should requeue if failed to create the composer deployment", func() { // given - serviceRepository.EXPECT().Create(requestContext, &composerComposerAPIService).Return(errFailed) + deploymentRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -841,9 +815,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the composer api service was created", func() { + It("Should return requeue if the composer deployment was created", func() { // given - serviceRepository.EXPECT().Create(requestContext, &composerComposerAPIService).Return(nil) + deploymentRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -852,34 +826,40 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("Composer API Service exists", func() { + Context("Composer Deployment exists", func() { const ( composerComposerAPIPortName = "composer-api" - composerWorkerAPIPortName = "worker-api" ) - var ( - composerWorkerAPIService corev1.Service + composerDeployment = appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: composerDeploymentName, + }, + } + + composerAPIExternalPort = intstr.FromInt(8080) - workerAPIExternalPort = intstr.FromInt(8700) + composerComposerAPIService corev1.Service ) + BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(&composerComposerAPIService, nil) + deploymentRepository.EXPECT().Read(requestContext, composerDeploymentName, operatorNamespace).Return(&composerDeployment, nil) - composerWorkerAPIService = corev1.Service{ + composerComposerAPIService = corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Namespace: operatorNamespace, - Name: composerWorkerAPIServiceName, + Name: composerComposerAPIServiceName, OwnerReferences: []metav1.OwnerReference{ownerReference}, }, Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeClusterIP, Ports: []corev1.ServicePort{ { - Name: composerWorkerAPIPortName, + Name: composerComposerAPIPortName, Port: 443, Protocol: "TCP", - TargetPort: workerAPIExternalPort, + TargetPort: composerAPIExternalPort, }, }, Selector: map[string]string{ @@ -889,9 +869,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { } }) - It("Should requeue if failed to get the worker api service", func() { + It("Should requeue if failed to get the composer api service", func() { // given - serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(nil, errFailed) + serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -899,14 +879,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Worker API Service not found", func() { + Context("Composer API Service not found", func() { BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(nil, errNotFound) + serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the worker api service", func() { + It("Should requeue if failed to create the composer api service", func() { // given - serviceRepository.EXPECT().Create(requestContext, &composerWorkerAPIService).Return(errFailed) + serviceRepository.EXPECT().Create(requestContext, &composerComposerAPIService).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -914,9 +894,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the worker api service was created", func() { + It("Should return requeue if the composer api service was created", func() { // given - serviceRepository.EXPECT().Create(requestContext, &composerWorkerAPIService).Return(nil) + serviceRepository.EXPECT().Create(requestContext, &composerComposerAPIService).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -925,18 +905,46 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("Worker API Service exists", func() { + Context("Composer API Service exists", func() { const ( - workerConfigAnsibleConfigConfigMapName = "osbuild-worker-setup-ansible-config" + composerComposerAPIPortName = "composer-api" + composerWorkerAPIPortName = "worker-api" ) + var ( + composerWorkerAPIService corev1.Service + + workerAPIExternalPort = intstr.FromInt(8700) + ) BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(&composerWorkerAPIService, nil) + serviceRepository.EXPECT().Read(requestContext, composerComposerAPIServiceName, operatorNamespace).Return(&composerComposerAPIService, nil) + + composerWorkerAPIService = corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: composerWorkerAPIServiceName, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Name: composerWorkerAPIPortName, + Port: 443, + Protocol: "TCP", + TargetPort: workerAPIExternalPort, + }, + }, + Selector: map[string]string{ + "app": "osbuild-composer", + }, + }, + } }) - It("Should requeue if failed to get the configMap for the ansible config ", func() { + It("Should requeue if failed to get the worker api service", func() { // given - configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(nil, errFailed) + serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -944,14 +952,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("ConfigMap for configuration ansible config not found", func() { + Context("Worker API Service not found", func() { BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(nil, errNotFound) + serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the configMap for the ansible config for the worker configuration job", func() { + It("Should requeue if failed to create the worker api service", func() { // given - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + serviceRepository.EXPECT().Create(requestContext, &composerWorkerAPIService).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -959,9 +967,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the configMap for the ansible config for the worker configuration job", func() { + It("Should return requeue if the worker api service was created", func() { // given - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + serviceRepository.EXPECT().Create(requestContext, &composerWorkerAPIService).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -970,27 +978,18 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("ConfigMap for the ansible config exists", func() { + Context("Worker API Service exists", func() { const ( - workerOSBuildWorkerConfigConfigMapName = "osbuild-worker-config" - ) - - var ( - workerConfigAnsibleConfigConfigMap = corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: workerConfigAnsibleConfigConfigMapName, - }, - } + workerConfigAnsibleConfigConfigMapName = "osbuild-worker-setup-ansible-config" ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(&workerConfigAnsibleConfigConfigMap, nil) + serviceRepository.EXPECT().Read(requestContext, composerWorkerAPIServiceName, operatorNamespace).Return(&composerWorkerAPIService, nil) }) - It("Should requeue if failed to get the configMap for the osbuild-worker config", func() { + It("Should requeue if failed to get the configMap for the ansible config ", func() { // given - configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -998,12 +997,12 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("ConfigMap for osbuild-worker config not found", func() { + Context("ConfigMap for configuration ansible config not found", func() { BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the configMap for the osbuild-worker config", func() { + It("Should requeue if failed to create the configMap for the ansible config for the worker configuration job", func() { // given configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when @@ -1013,7 +1012,7 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the configMap for the osbuild-worker config", func() { + It("Should return requeue if succeeded to create the configMap for the ansible config for the worker configuration job", func() { // given configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when @@ -1024,46 +1023,27 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("ConfigMap for the osbuild-worker config exists", func() { + Context("ConfigMap for the ansible config exists", func() { const ( - workerSSHKeysSecretName = "osbuild-worker-ssh" // #nosec G101 - workerSSHKeysPrivateKeyKey = "ssh-privatekey" - workerSSHKeysPublicKeyKey = "ssh-publickey" + workerOSBuildWorkerConfigConfigMapName = "osbuild-worker-config" ) var ( - workerOSBuildWorkerConfigConfigMap = corev1.ConfigMap{ + workerConfigAnsibleConfigConfigMap = corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: operatorNamespace, - Name: workerOSBuildWorkerConfigConfigMapName, + Name: workerConfigAnsibleConfigConfigMapName, }, } - - sshPrivateKey = []byte("sshPrivateKey") - sshPublicKey = []byte("sshPublicKey") - - workerSSHKeysSecret *corev1.Secret ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(&workerOSBuildWorkerConfigConfigMap, nil) - workerSSHKeysSecret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: workerSSHKeysSecretName, - Namespace: operatorNamespace, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Immutable: pointer.Bool(true), - StringData: map[string]string{ - workerSSHKeysPrivateKeyKey: string(sshPrivateKey), - workerSSHKeysPublicKeyKey: string(sshPublicKey), - }, - } + configMapRepository.EXPECT().Read(requestContext, workerConfigAnsibleConfigConfigMapName, operatorNamespace).Return(&workerConfigAnsibleConfigConfigMap, nil) }) - It("Should requeue if failed to get the secret for the osbuild-worker ssh keys", func() { + It("Should requeue if failed to get the configMap for the osbuild-worker config", func() { // given - secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1071,15 +1051,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Secret for osbuild-worker ssh keys not found", func() { + Context("ConfigMap for osbuild-worker config not found", func() { BeforeEach(func() { - secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(nil, errNotFound) }) - It("Should return en error if failed to create the ssh keys", func() { + It("Should requeue if failed to create the configMap for the osbuild-worker config", func() { // given - sshGenerateError := fmt.Errorf("failed to generate ssh keys") - sshKeyGenerator.EXPECT().GenerateSSHKeyPair().Return(nil, nil, sshGenerateError) + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1087,41 +1066,57 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Successfully created the ssh keys", func() { - BeforeEach(func() { - sshKeyGenerator.EXPECT().GenerateSSHKeyPair().Return(sshPrivateKey, sshPublicKey, nil) - }) - - It("Should requeue if failed to create the secret for the osbuild-worker ssh keys", func() { - // given - secretRepository.EXPECT().Create(requestContext, workerSSHKeysSecret).Return(errFailed) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) - }) - - It("Should return requeue if succeeded to create the secret for the osbuild-worker ssh keys", func() { - // given - secretRepository.EXPECT().Create(requestContext, workerSSHKeysSecret).Return(nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) + It("Should return requeue if succeeded to create the configMap for the osbuild-worker config", func() { + // given + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) }) }) - Context("Secret for osbuild-worker ssh keys exists", func() { + Context("ConfigMap for the osbuild-worker config exists", func() { + const ( + workerSSHKeysSecretName = "osbuild-worker-ssh" // #nosec G101 + workerSSHKeysPrivateKeyKey = "ssh-privatekey" + workerSSHKeysPublicKeyKey = "ssh-publickey" + ) + + var ( + workerOSBuildWorkerConfigConfigMap = corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: workerOSBuildWorkerConfigConfigMapName, + }, + } + + sshPrivateKey = []byte("sshPrivateKey") + sshPublicKey = []byte("sshPublicKey") + + workerSSHKeysSecret *corev1.Secret + ) + BeforeEach(func() { - secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(workerSSHKeysSecret, nil) + configMapRepository.EXPECT().Read(requestContext, workerOSBuildWorkerConfigConfigMapName, operatorNamespace).Return(&workerOSBuildWorkerConfigConfigMap, nil) + workerSSHKeysSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: workerSSHKeysSecretName, + Namespace: operatorNamespace, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Immutable: pointer.Bool(true), + StringData: map[string]string{ + workerSSHKeysPrivateKeyKey: string(sshPrivateKey), + workerSSHKeysPublicKeyKey: string(sshPublicKey), + }, + } }) - It("Should requeue if failed to get the VirtualMachine for the internal builder", func() { + It("Should requeue if failed to get the secret for the osbuild-worker ssh keys", func() { // given - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errFailed) + secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1129,14 +1124,15 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("VirtualMachine for the internal builder not found", func() { + Context("Secret for osbuild-worker ssh keys not found", func() { BeforeEach(func() { - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errNotFound) + secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the VirtualMachine for the internal builder", func() { + It("Should return en error if failed to create the ssh keys", func() { // given - virtualMachineRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + sshGenerateError := fmt.Errorf("failed to generate ssh keys") + sshKeyGenerator.EXPECT().GenerateSSHKeyPair().Return(nil, nil, sshGenerateError) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1144,64 +1140,41 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the VirtualMachine for the internal builder", func() { - // given - virtualMachineRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) - }) + Context("Successfully created the ssh keys", func() { + BeforeEach(func() { + sshKeyGenerator.EXPECT().GenerateSSHKeyPair().Return(sshPrivateKey, sshPublicKey, nil) + }) - Context("VirtualMachine for the internal builder exists", func() { - const ( - workerSSHServiceNameFormat = "worker-%s-ssh" - workerSSHPortName = "ssh" - ) + It("Should requeue if failed to create the secret for the osbuild-worker ssh keys", func() { + // given + secretRepository.EXPECT().Create(requestContext, workerSSHKeysSecret).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) - var ( - workerSSHServiceName = fmt.Sprintf(workerSSHServiceNameFormat, internalBuilderName) - internalBuilderVM *kubevirtv1.VirtualMachine - workerSSHService *corev1.Service - ) + It("Should return requeue if succeeded to create the secret for the osbuild-worker ssh keys", func() { + // given + secretRepository.EXPECT().Create(requestContext, workerSSHKeysSecret).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + }) + }) + }) + Context("Secret for osbuild-worker ssh keys exists", func() { BeforeEach(func() { - internalBuilderVM = &kubevirtv1.VirtualMachine{ - ObjectMeta: metav1.ObjectMeta{ - Name: internalBuilderName, - Namespace: operatorNamespace, - }, - } - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) - - workerSSHService = &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: workerSSHServiceName, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeClusterIP, - Ports: []corev1.ServicePort{ - { - Name: workerSSHPortName, - Port: 22, - Protocol: "TCP", - TargetPort: intstr.FromInt(22), - }, - }, - Selector: map[string]string{ - "osbuild-worker": internalBuilderName, - }, - }, - } + secretRepository.EXPECT().Read(requestContext, workerSSHKeysSecretName, operatorNamespace).Return(workerSSHKeysSecret, nil) }) - It("Should requeue if failed to get the ssh service of the internal builder", func() { + It("Should requeue if failed to get the VirtualMachine for the internal builder", func() { // given - serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(nil, errFailed) + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1209,14 +1182,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("SSH Service for the internal builder not found", func() { + Context("VirtualMachine for the internal builder not found", func() { BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(nil, errNotFound) + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the SSH Service for the internal builder", func() { + It("Should requeue if failed to create the VirtualMachine for the internal builder", func() { // given - serviceRepository.EXPECT().Create(requestContext, workerSSHService).Return(errFailed) + virtualMachineRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1224,9 +1197,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the SSH Service for the internal builder", func() { + It("Should return requeue if succeeded to create the VirtualMachine for the internal builder", func() { // given - serviceRepository.EXPECT().Create(requestContext, workerSSHService).Return(nil) + virtualMachineRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1235,14 +1208,53 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("SSH Service for the internal builder exists", func() { + Context("VirtualMachine for the internal builder exists", func() { + const ( + workerSSHServiceNameFormat = "worker-%s-ssh" + workerSSHPortName = "ssh" + ) + + var ( + workerSSHServiceName = fmt.Sprintf(workerSSHServiceNameFormat, internalBuilderName) + internalBuilderVM *kubevirtv1.VirtualMachine + workerSSHService *corev1.Service + ) + BeforeEach(func() { - serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(workerSSHService, nil) + internalBuilderVM = &kubevirtv1.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: internalBuilderName, + Namespace: operatorNamespace, + }, + } + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) + + workerSSHService = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: workerSSHServiceName, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Name: workerSSHPortName, + Port: 22, + Protocol: "TCP", + TargetPort: intstr.FromInt(22), + }, + }, + Selector: map[string]string{ + "osbuild-worker": internalBuilderName, + }, + }, + } }) - It("Should fail if failed to get the VM of the internal worker", func() { + It("Should requeue if failed to get the ssh service of the internal builder", func() { // given - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errFailed) + serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1250,64 +1262,40 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if the internal worker VM is not ready", func() { - // given - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) + Context("SSH Service for the internal builder not found", func() { + BeforeEach(func() { + serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(nil, errNotFound) + }) - Context("Internal worker VM is ready", func() { - const ( - workerCertificateNameFormat = "worker-%s-cert" - ) + It("Should requeue if failed to create the SSH Service for the internal builder", func() { + // given + serviceRepository.EXPECT().Create(requestContext, workerSSHService).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) - var ( - internalBuilderCertificateName = fmt.Sprintf(workerCertificateNameFormat, internalBuilderName) - internalBuilderCertificate *certmanagerv1.Certificate - ) + It("Should return requeue if succeeded to create the SSH Service for the internal builder", func() { + // given + serviceRepository.EXPECT().Create(requestContext, workerSSHService).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + }) + }) + Context("SSH Service for the internal builder exists", func() { BeforeEach(func() { - internalBuilderVM.Status.Conditions = []kubevirtv1.VirtualMachineCondition{ - { - Type: kubevirtv1.VirtualMachineReady, - Status: corev1.ConditionTrue, - }, - } - virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) - internalBuilderCertificate = &certmanagerv1.Certificate{ - ObjectMeta: metav1.ObjectMeta{ - Name: internalBuilderCertificateName, - Namespace: operatorNamespace, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: certmanagerv1.CertificateSpec{ - SecretName: internalBuilderCertificateName, - PrivateKey: &certmanagerv1.CertificatePrivateKey{ - Algorithm: "ECDSA", - Size: 256, - }, - DNSNames: []string{ - internalBuilderName, - }, - Duration: &metav1.Duration{ - Duration: time.Hour * certificateDuration, - }, - IssuerRef: certmanagermetav1.ObjectReference{ - Group: "cert-manager.io", - Kind: "Issuer", - Name: caIssuerName, - }, - }, - } + serviceRepository.EXPECT().Read(requestContext, workerSSHServiceName, operatorNamespace).Return(workerSSHService, nil) }) - It("Should requeue if failed to get the certificate of the internal builder", func() { + It("Should fail if failed to get the VM of the internal worker", func() { // given - certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errFailed) + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1315,49 +1303,64 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("Certificate for the internal builder not found", func() { - BeforeEach(func() { - certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errNotFound) - }) - - It("Should requeue if failed to create the Certificate for the internal builder", func() { - // given - certificateRepository.EXPECT().Create(requestContext, internalBuilderCertificate).Return(errFailed) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultRequeue)) - }) - - It("Should return requeue if succeeded to create the Certificate for the internal builder", func() { - // given - certificateRepository.EXPECT().Create(requestContext, internalBuilderCertificate).Return(nil) - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultQuickRequeue)) - }) + It("Should return requeue if the internal worker VM is not ready", func() { + // given + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) }) - Context("Certificate for the internal builder exists", func() { + Context("Internal worker VM is ready", func() { + const ( + workerCertificateNameFormat = "worker-%s-cert" + ) + var ( - internalBuilderCertificateSecret *corev1.Secret + internalBuilderCertificateName = fmt.Sprintf(workerCertificateNameFormat, internalBuilderName) + internalBuilderCertificate *certmanagerv1.Certificate ) + BeforeEach(func() { - certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificate, nil) - internalBuilderCertificateSecret = &corev1.Secret{ + internalBuilderVM.Status.Conditions = []kubevirtv1.VirtualMachineCondition{ + { + Type: kubevirtv1.VirtualMachineReady, + Status: corev1.ConditionTrue, + }, + } + virtualMachineRepository.EXPECT().Read(requestContext, internalBuilderName, operatorNamespace).Return(internalBuilderVM, nil) + internalBuilderCertificate = &certmanagerv1.Certificate{ ObjectMeta: metav1.ObjectMeta{ - Namespace: operatorNamespace, - Name: internalBuilderCertificateName, + Name: internalBuilderCertificateName, + Namespace: operatorNamespace, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + Spec: certmanagerv1.CertificateSpec{ + SecretName: internalBuilderCertificateName, + PrivateKey: &certmanagerv1.CertificatePrivateKey{ + Algorithm: "ECDSA", + Size: 256, + }, + DNSNames: []string{ + internalBuilderName, + }, + Duration: &metav1.Duration{ + Duration: time.Hour * certificateDuration, + }, + IssuerRef: certmanagermetav1.ObjectReference{ + Group: "cert-manager.io", + Kind: "Issuer", + Name: caIssuerName, + }, }, } }) - It("Should requeue if failed to get the secret of the certificate of the internal builder", func() { + It("Should requeue if failed to get the certificate of the internal builder", func() { // given - secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errFailed) + certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1365,23 +1368,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("The Secret of the Certificate for the internal builder is not owned by the instance", func() { - var ( - originalInternalBuilderCertificateSecret *corev1.Secret - updatedInternalBuilderCertificateSecret *corev1.Secret - ) - + Context("Certificate for the internal builder not found", func() { BeforeEach(func() { - secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificateSecret, nil) - - originalInternalBuilderCertificateSecret = internalBuilderCertificateSecret.DeepCopy() - updatedInternalBuilderCertificateSecret = internalBuilderCertificateSecret.DeepCopy() - updatedInternalBuilderCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} + certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to own the secret of the Certificate for the internal builder", func() { + It("Should requeue if failed to create the Certificate for the internal builder", func() { // given - secretRepository.EXPECT().Patch(requestContext, originalInternalBuilderCertificateSecret, updatedInternalBuilderCertificateSecret).Return(errFailed) + certificateRepository.EXPECT().Create(requestContext, internalBuilderCertificate).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1389,9 +1383,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to set the owner of the secret of the Certificate for the internal builder", func() { + It("Should return requeue if succeeded to create the Certificate for the internal builder", func() { // given - secretRepository.EXPECT().Patch(requestContext, originalInternalBuilderCertificateSecret, updatedInternalBuilderCertificateSecret).Return(nil) + certificateRepository.EXPECT().Create(requestContext, internalBuilderCertificate).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1400,21 +1394,23 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("Internal Builder Certificate Secret Owner is already set", func() { - const ( - workerSetupPlaybookConfigMapNameFormat = "worker-%s-setup-playbook" - ) + Context("Certificate for the internal builder exists", func() { var ( - internalBuilderSetupPlaybookConfigMapName = fmt.Sprintf(workerSetupPlaybookConfigMapNameFormat, internalBuilderName) + internalBuilderCertificateSecret *corev1.Secret ) BeforeEach(func() { - internalBuilderCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} - secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificateSecret, nil) + certificateRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificate, nil) + internalBuilderCertificateSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: internalBuilderCertificateName, + }, + } }) - It("Should requeue if failed to get the configMap for the setup playbook of the internal builder", func() { + It("Should requeue if failed to get the secret of the certificate of the internal builder", func() { // given - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(nil, errFailed) + secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1422,14 +1418,23 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("The configMap for the setup playbook for the internal builder not found", func() { + Context("The Secret of the Certificate for the internal builder is not owned by the instance", func() { + var ( + originalInternalBuilderCertificateSecret *corev1.Secret + updatedInternalBuilderCertificateSecret *corev1.Secret + ) + BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(nil, errNotFound) + secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificateSecret, nil) + + originalInternalBuilderCertificateSecret = internalBuilderCertificateSecret.DeepCopy() + updatedInternalBuilderCertificateSecret = internalBuilderCertificateSecret.DeepCopy() + updatedInternalBuilderCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} }) - It("Should requeue if failed to create the configMap for the setup playbook for the internal builder", func() { + It("Should requeue if failed to own the secret of the Certificate for the internal builder", func() { // given - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + secretRepository.EXPECT().Patch(requestContext, originalInternalBuilderCertificateSecret, updatedInternalBuilderCertificateSecret).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1437,9 +1442,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the configMap for the setup playbook for the internal builder", func() { + It("Should return requeue if succeeded to set the owner of the secret of the Certificate for the internal builder", func() { // given - configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + secretRepository.EXPECT().Patch(requestContext, originalInternalBuilderCertificateSecret, updatedInternalBuilderCertificateSecret).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1448,27 +1453,21 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("The configMap for the setup playbook for the internal builder exists", func() { + Context("Internal Builder Certificate Secret Owner is already set", func() { const ( - workerSetupInventoryConfigMapNameFormat = "worker-%s-inventory" + workerSetupPlaybookConfigMapNameFormat = "worker-%s-setup-playbook" ) var ( - internalBuilderSetupInventoryConfigMapName = fmt.Sprintf(workerSetupInventoryConfigMapNameFormat, internalBuilderName) - internalBuilderSetupPlaybookConfigMap = &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: internalBuilderSetupPlaybookConfigMapName, - Namespace: operatorNamespace, - }, - } + internalBuilderSetupPlaybookConfigMapName = fmt.Sprintf(workerSetupPlaybookConfigMapNameFormat, internalBuilderName) ) - BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(internalBuilderSetupPlaybookConfigMap, nil) + internalBuilderCertificateSecret.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerReference} + secretRepository.EXPECT().Read(requestContext, internalBuilderCertificateName, operatorNamespace).Return(internalBuilderCertificateSecret, nil) }) - It("Should requeue if failed to get the configMap for the setup inventory of the internal builder", func() { + It("Should requeue if failed to get the configMap for the setup playbook of the internal builder", func() { // given - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1478,10 +1477,10 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Context("The configMap for the setup playbook for the internal builder not found", func() { BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the configMap for the setup inventory for the internal builder", func() { + It("Should requeue if failed to create the configMap for the setup playbook for the internal builder", func() { // given configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when @@ -1491,7 +1490,7 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the configMap for the setup inventory for the internal builder", func() { + It("Should return requeue if succeeded to create the configMap for the setup playbook for the internal builder", func() { // given configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when @@ -1502,27 +1501,27 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("The configMap for the setup inventory for the internal builder exists", func() { + Context("The configMap for the setup playbook for the internal builder exists", func() { const ( - workerSetupJobNameFormat = "worker-%s-setup" + workerSetupInventoryConfigMapNameFormat = "worker-%s-inventory" ) var ( - internalBuilderSetupJobName = fmt.Sprintf(workerSetupJobNameFormat, internalBuilderName) - internalBuilderSetupInventoryConfigMap = &corev1.ConfigMap{ + internalBuilderSetupInventoryConfigMapName = fmt.Sprintf(workerSetupInventoryConfigMapNameFormat, internalBuilderName) + internalBuilderSetupPlaybookConfigMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: internalBuilderSetupInventoryConfigMapName, + Name: internalBuilderSetupPlaybookConfigMapName, Namespace: operatorNamespace, }, } ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(internalBuilderSetupInventoryConfigMap, nil) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(internalBuilderSetupPlaybookConfigMap, nil) }) - It("Should requeue if failed to get the job for the setup of the internal builder", func() { + It("Should requeue if failed to get the configMap for the setup inventory of the internal builder", func() { // given - jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(nil, errFailed) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(nil, errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1530,14 +1529,14 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - Context("The job for the setup for the internal builder not found", func() { + Context("The configMap for the setup playbook for the internal builder not found", func() { BeforeEach(func() { - jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(nil, errNotFound) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(nil, errNotFound) }) - It("Should requeue if failed to create the job for the setup for the internal builder", func() { + It("Should requeue if failed to create the configMap for the setup inventory for the internal builder", func() { // given - jobRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1545,9 +1544,9 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { Expect(result).To(Equal(resultRequeue)) }) - It("Should return requeue if succeeded to create the job for the setup for the internal builder", func() { + It("Should return requeue if succeeded to create the configMap for the setup inventory for the internal builder", func() { // given - jobRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + configMapRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) // when result, err := reconciler.Reconcile(requestContext, request) // then @@ -1556,130 +1555,289 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) - Context("The job for the setup for the internal builder exists", func() { + Context("The configMap for the setup inventory for the internal builder exists", func() { + const ( + workerSetupJobNameFormat = "worker-%s-setup" + ) var ( - internalBuilderSetupJob = &batchv1.Job{ + internalBuilderSetupJobName = fmt.Sprintf(workerSetupJobNameFormat, internalBuilderName) + internalBuilderSetupInventoryConfigMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: internalBuilderSetupJobName, + Name: internalBuilderSetupInventoryConfigMapName, Namespace: operatorNamespace, }, } ) BeforeEach(func() { - jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(internalBuilderSetupJob, nil) + configMapRepository.EXPECT().Read(requestContext, internalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(internalBuilderSetupInventoryConfigMap, nil) }) - Context("The certificate for the external builder exists", func() { - var ( - externalBuilderCertificateName = fmt.Sprintf(workerCertificateNameFormat, externalBuilderName) - externalBuilderCertificate *certmanagerv1.Certificate - ) + It("Should requeue if failed to get the job for the setup of the internal builder", func() { + // given + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(nil, errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) + Context("The job for the setup for the internal builder not found", func() { BeforeEach(func() { - externalBuilderCertificate = &certmanagerv1.Certificate{ + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(nil, errNotFound) + }) + + It("Should requeue if failed to create the job for the setup for the internal builder", func() { + // given + jobRepository.EXPECT().Create(requestContext, gomock.Any()).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) + + It("Should return requeue if succeeded to create the job for the setup for the internal builder", func() { + // given + jobRepository.EXPECT().Create(requestContext, gomock.Any()).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + }) + }) + + Context("The job for the setup for the internal builder exists", func() { + var ( + internalBuilderSetupJob = &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ - Name: externalBuilderCertificateName, - Namespace: operatorNamespace, - OwnerReferences: []metav1.OwnerReference{ownerReference}, - }, - Spec: certmanagerv1.CertificateSpec{ - SecretName: externalBuilderCertificateName, - PrivateKey: &certmanagerv1.CertificatePrivateKey{ - Algorithm: "ECDSA", - Size: 256, - }, - DNSNames: []string{ - externalBuilderName, - }, - Duration: &metav1.Duration{ - Duration: time.Hour * certificateDuration, - }, - IssuerRef: certmanagermetav1.ObjectReference{ - Group: "cert-manager.io", - Kind: "Issuer", - Name: caIssuerName, - }, + Name: internalBuilderSetupJobName, + Namespace: operatorNamespace, }, } - certificateRepository.EXPECT().Read(requestContext, externalBuilderCertificateName, operatorNamespace).Return(externalBuilderCertificate, nil) + ) + + BeforeEach(func() { + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(internalBuilderSetupJob, nil) }) - Context("The Secret of the certificate of the external worker is owned by the instance", func() { + Context("The certificate for the external builder exists", func() { var ( - externalBuilderCertificateSecret *corev1.Secret + externalBuilderCertificateName = fmt.Sprintf(workerCertificateNameFormat, externalBuilderName) + externalBuilderCertificate *certmanagerv1.Certificate ) BeforeEach(func() { - externalBuilderCertificateSecret = &corev1.Secret{ + externalBuilderCertificate = &certmanagerv1.Certificate{ ObjectMeta: metav1.ObjectMeta{ + Name: externalBuilderCertificateName, Namespace: operatorNamespace, - Name: internalBuilderCertificateName, OwnerReferences: []metav1.OwnerReference{ownerReference}, }, + Spec: certmanagerv1.CertificateSpec{ + SecretName: externalBuilderCertificateName, + PrivateKey: &certmanagerv1.CertificatePrivateKey{ + Algorithm: "ECDSA", + Size: 256, + }, + DNSNames: []string{ + externalBuilderName, + }, + Duration: &metav1.Duration{ + Duration: time.Hour * certificateDuration, + }, + IssuerRef: certmanagermetav1.ObjectReference{ + Group: "cert-manager.io", + Kind: "Issuer", + Name: caIssuerName, + }, + }, } - secretRepository.EXPECT().Read(requestContext, externalBuilderCertificateName, operatorNamespace).Return(externalBuilderCertificateSecret, nil) + certificateRepository.EXPECT().Read(requestContext, externalBuilderCertificateName, operatorNamespace).Return(externalBuilderCertificate, nil) }) - Context("The ConfigMap for the playbook of the external builder setup exists", func() { + Context("The Secret of the certificate of the external worker is owned by the instance", func() { var ( - externalBuilderSetupPlaybookConfigMapName = fmt.Sprintf(workerSetupPlaybookConfigMapNameFormat, externalBuilderName) - externalBuilderSetupPlaybookConfigMap = &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: externalBuilderSetupPlaybookConfigMapName, - Namespace: operatorNamespace, - }, - } + externalBuilderCertificateSecret *corev1.Secret ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, externalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(externalBuilderSetupPlaybookConfigMap, nil) + externalBuilderCertificateSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: operatorNamespace, + Name: internalBuilderCertificateName, + OwnerReferences: []metav1.OwnerReference{ownerReference}, + }, + } + secretRepository.EXPECT().Read(requestContext, externalBuilderCertificateName, operatorNamespace).Return(externalBuilderCertificateSecret, nil) }) - Context("The ConfigMap for the inventory of the external builder setup exists", func() { + Context("The ConfigMap for the playbook of the external builder setup exists", func() { var ( - externalBuilderSetupInventoryConfigMapName = fmt.Sprintf(workerSetupInventoryConfigMapNameFormat, externalBuilderName) - externalBuilderSetupInventoryConfigMap = &corev1.ConfigMap{ + externalBuilderSetupPlaybookConfigMapName = fmt.Sprintf(workerSetupPlaybookConfigMapNameFormat, externalBuilderName) + externalBuilderSetupPlaybookConfigMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: externalBuilderSetupInventoryConfigMapName, + Name: externalBuilderSetupPlaybookConfigMapName, Namespace: operatorNamespace, }, } ) BeforeEach(func() { - configMapRepository.EXPECT().Read(requestContext, externalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(externalBuilderSetupInventoryConfigMap, nil) + configMapRepository.EXPECT().Read(requestContext, externalBuilderSetupPlaybookConfigMapName, operatorNamespace).Return(externalBuilderSetupPlaybookConfigMap, nil) }) - Context("The job for the external builder setup exists", func() { + Context("The ConfigMap for the inventory of the external builder setup exists", func() { var ( - externalBuilderSetupJobName = fmt.Sprintf(workerSetupJobNameFormat, externalBuilderName) - externalBuilderSetupJob = &batchv1.Job{ + externalBuilderSetupInventoryConfigMapName = fmt.Sprintf(workerSetupInventoryConfigMapNameFormat, externalBuilderName) + externalBuilderSetupInventoryConfigMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: externalBuilderSetupJobName, + Name: externalBuilderSetupInventoryConfigMapName, Namespace: operatorNamespace, }, } ) BeforeEach(func() { - jobRepository.EXPECT().Read(requestContext, externalBuilderSetupJobName, operatorNamespace).Return(externalBuilderSetupJob, nil) + configMapRepository.EXPECT().Read(requestContext, externalBuilderSetupInventoryConfigMapName, operatorNamespace).Return(externalBuilderSetupInventoryConfigMap, nil) }) - It("Should return Done", func() { - // when - result, err := reconciler.Reconcile(requestContext, request) - // then - Expect(err).To(BeNil()) - Expect(result).To(Equal(resultDone)) + Context("The job for the external builder setup exists", func() { + var ( + externalBuilderSetupJobName = fmt.Sprintf(workerSetupJobNameFormat, externalBuilderName) + externalBuilderSetupJob = &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: externalBuilderSetupJobName, + Namespace: operatorNamespace, + }, + } + ) + + BeforeEach(func() { + jobRepository.EXPECT().Read(requestContext, externalBuilderSetupJobName, operatorNamespace).Return(externalBuilderSetupJob, nil) + }) + + It("Should return requeue if failed to get internal builder setup job", func() { + // given + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(nil, errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) + + It("Should return done if the internal builder setup job is still running", func() { + // given + internalBuilderSetupJob.Status.Active = 1 + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(internalBuilderSetupJob, nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultDone)) + }) + + Context("The internal builder setup job is done", func() { + BeforeEach(func() { + internalBuilderSetupJob.Status.Active = 0 + internalBuilderSetupJob.Status.Conditions = append( + internalBuilderSetupJob.Status.Conditions, + batchv1.JobCondition{ + Type: batchv1.JobComplete, + Status: corev1.ConditionTrue, + }, + ) + jobRepository.EXPECT().Read(requestContext, internalBuilderSetupJobName, operatorNamespace).Return(internalBuilderSetupJob, nil) + }) + + Context("The external builder setup job is done", func() { + var ( + conditionsReady = []osbuildv1alpha1.Condition{ + { + Type: osbuildv1alpha1.ConditionInProgress, + Status: metav1.ConditionFalse, + }, + { + Type: osbuildv1alpha1.ConditionFailed, + Status: metav1.ConditionFalse, + }, + { + Type: osbuildv1alpha1.ConditionReady, + Status: metav1.ConditionTrue, + }, + } + ) + + BeforeEach(func() { + externalBuilderSetupJob.Status.Active = 0 + externalBuilderSetupJob.Status.Conditions = append( + internalBuilderSetupJob.Status.Conditions, + batchv1.JobCondition{ + Type: batchv1.JobComplete, + Status: corev1.ConditionTrue, + }, + ) + jobRepository.EXPECT().Read(requestContext, externalBuilderSetupJobName, operatorNamespace).Return(externalBuilderSetupJob, nil) + }) + + Context("Need to updated conditions", func() { + var ( + originalInstance *osbuildv1alpha1.OSBuildEnvConfig + ) + + BeforeEach(func() { + originalInstance = instance.DeepCopy() + }) + + AfterEach(func() { + instance.Status.Conditions = append([]osbuildv1alpha1.Condition{}, conditionsInProgress...) + }) + + It("Should return requeue if failed to update the conditions", func() { + // given + osBuildEnvConfigRepository.EXPECT().PatchStatus(requestContext, originalInstance, &instance).Return(errFailed) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultRequeue)) + }) + + It("Should return requeue if the conditions were updated", func() { + validateOSBuildEnvConfigConditionArr(instance.Status.Conditions, osbuildv1alpha1.ConditionInProgress) + // given + osBuildEnvConfigRepository.EXPECT().PatchStatus(requestContext, originalInstance, &instance).Return(nil) + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultQuickRequeue)) + validateOSBuildEnvConfigConditionArr(instance.Status.Conditions, osbuildv1alpha1.ConditionReady) + }) + }) + + Context("The Condition are already updated", func() { + BeforeEach(func() { + instance.Status.Conditions = append([]osbuildv1alpha1.Condition{}, conditionsReady...) + }) + + It("Should return Done", func() { + // when + result, err := reconciler.Reconcile(requestContext, request) + // then + Expect(err).To(BeNil()) + Expect(result).To(Equal(resultDone)) + }) + }) + }) + }) }) - }) - }) - }) - }) }) }) @@ -1706,3 +1864,13 @@ var _ = Describe("OSBuildEnvConfig Controller", func() { }) }) }) + +func validateOSBuildEnvConfigConditionArr(conditions []osbuildv1alpha1.Condition, requiredStatus osbuildv1alpha1.ConditionType) { + for _, c := range conditions { + if c.Type == requiredStatus { + Expect(c.Status).To(Equal(metav1.ConditionTrue)) + } else { + Expect(c.Status).To(Equal(metav1.ConditionFalse)) + } + } +} diff --git a/go.sum b/go.sum index 441a3d71..f5fc7864 100644 --- a/go.sum +++ b/go.sum @@ -480,9 +480,11 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/getkin/kin-openapi v0.94.0 h1:bAxg2vxgnHHHoeefVdmGbR+oxtJlcv5HsJJa3qmAHuo= github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac1BUPam4aw6Q= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= @@ -898,7 +900,9 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI= github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= +github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= @@ -942,12 +946,14 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -1310,7 +1316,9 @@ github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.14.1 h1:0Sx+C9404t2+DPuIJ3UpZFOEFhNG3wPxMj7uZHyZKFA= github.com/urfave/cli/v2 v2.14.1/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= @@ -1499,6 +1507,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1859,6 +1868,7 @@ golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.11-0.20220322213029-87a8611856c1/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/predicates/osbuildenvconfig_job.go b/internal/predicates/osbuildenvconfig_job.go new file mode 100644 index 00000000..0d1c627d --- /dev/null +++ b/internal/predicates/osbuildenvconfig_job.go @@ -0,0 +1,40 @@ +package predicates + +import ( + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + batchv1 "k8s.io/api/batch/v1" +) + +type OSBuildEnvConfigJobFinished struct { + predicate.Funcs +} + +func (OSBuildEnvConfigJobFinished) Create(e event.CreateEvent) bool { + return false +} + +func (OSBuildEnvConfigJobFinished) Delete(e event.DeleteEvent) bool { + return false +} + +func (OSBuildEnvConfigJobFinished) Generic(e event.GenericEvent) bool { + return false +} + +func (OSBuildEnvConfigJobFinished) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil { + return false + } + if e.ObjectNew == nil { + return false + } + + newJob, ok := e.ObjectNew.(*batchv1.Job) + if !ok { + return false + } + + return newJob.Status.Active == 0 +} diff --git a/internal/predicates/osbuildenvconfig_job_test.go b/internal/predicates/osbuildenvconfig_job_test.go new file mode 100644 index 00000000..b5f56dc5 --- /dev/null +++ b/internal/predicates/osbuildenvconfig_job_test.go @@ -0,0 +1,144 @@ +package predicates_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + + batchv1 "k8s.io/api/batch/v1" + + "github.com/project-flotta/osbuild-operator/internal/predicates" +) + +var _ = Describe("OSBuildEnvConfig Job reconciliation predicate", func() { + DescribeTable("should reconcile update", func(old, new runtimeclient.Object) { + // given + p := predicates.OSBuildEnvConfigJobFinished{} + e := event.UpdateEvent{ObjectOld: old, ObjectNew: new} + + // when + shouldReconcile := p.Update(e) + + // then + Expect(shouldReconcile).To(BeTrue()) + }, + Entry("when there are no active pods", + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 0, + }, + }, + ), + ) + + DescribeTable("should not reconcile update", func(old, new runtimeclient.Object) { + // given + p := predicates.OSBuildEnvConfigJobFinished{} + e := event.UpdateEvent{ObjectOld: old, ObjectNew: new} + + // when + shouldReconcile := p.Update(e) + + // then + Expect(shouldReconcile).To(BeFalse()) + }, + Entry("when there are still active pods", + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + ), + Entry("when old is missing", + nil, + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + ), + Entry("when new is missing", + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + nil, + ), + Entry("when new is not Job", + &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + &batchv1.CronJob{}, + ), + ) + + It("should not reconcile create", func() { + // given + p := predicates.OSBuildEnvConfigJobFinished{} + e := event.CreateEvent{ + Object: &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + } + + // when + shouldReconcile := p.Create(e) + + // then + Expect(shouldReconcile).To(BeFalse()) + }) + + It("should not reconcile delete", func() { + // given + p := predicates.OSBuildEnvConfigJobFinished{} + e := event.DeleteEvent{ + Object: &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + } + + // when + shouldReconcile := p.Delete(e) + + // then + Expect(shouldReconcile).To(BeFalse()) + }) + + It("should not reconcile for generic event", func() { + // given + // given + p := predicates.OSBuildEnvConfigJobFinished{} + e := event.GenericEvent{ + Object: &batchv1.Job{ + Status: batchv1.JobStatus{ + Active: 1, + }, + }, + } + + // when + shouldReconcile := p.Generic(e) + + // then + Expect(shouldReconcile).To(BeFalse()) + }) +}) diff --git a/internal/repository/osbuildenvconfig/mock_osbuildenvconfig.go b/internal/repository/osbuildenvconfig/mock_osbuildenvconfig.go index 365568c4..ae460c39 100644 --- a/internal/repository/osbuildenvconfig/mock_osbuildenvconfig.go +++ b/internal/repository/osbuildenvconfig/mock_osbuildenvconfig.go @@ -49,6 +49,20 @@ func (mr *MockRepositoryMockRecorder) Patch(arg0, arg1, arg2 interface{}) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Patch", reflect.TypeOf((*MockRepository)(nil).Patch), arg0, arg1, arg2) } +// PatchStatus mocks base method. +func (m *MockRepository) PatchStatus(arg0 context.Context, arg1, arg2 *v1alpha1.OSBuildEnvConfig) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PatchStatus", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// PatchStatus indicates an expected call of PatchStatus. +func (mr *MockRepositoryMockRecorder) PatchStatus(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchStatus", reflect.TypeOf((*MockRepository)(nil).PatchStatus), arg0, arg1, arg2) +} + // Read mocks base method. func (m *MockRepository) Read(arg0 context.Context, arg1 string) (*v1alpha1.OSBuildEnvConfig, error) { m.ctrl.T.Helper() diff --git a/internal/repository/osbuildenvconfig/osbuildenvconfig.go b/internal/repository/osbuildenvconfig/osbuildenvconfig.go index 662d40a8..25ea51d9 100644 --- a/internal/repository/osbuildenvconfig/osbuildenvconfig.go +++ b/internal/repository/osbuildenvconfig/osbuildenvconfig.go @@ -14,6 +14,7 @@ import ( type Repository interface { Read(ctx context.Context, name string) (*v1alpha1.OSBuildEnvConfig, error) Patch(ctx context.Context, old, new *v1alpha1.OSBuildEnvConfig) error + PatchStatus(ctx context.Context, old, new *v1alpha1.OSBuildEnvConfig) error } type CRRepository struct { @@ -34,3 +35,8 @@ func (r *CRRepository) Patch(ctx context.Context, old, new *v1alpha1.OSBuildEnvC patch := client.MergeFrom(old) return r.client.Patch(ctx, new, patch) } + +func (r *CRRepository) PatchStatus(ctx context.Context, old, new *v1alpha1.OSBuildEnvConfig) error { + patch := client.MergeFrom(old) + return r.client.Status().Patch(ctx, new, patch) +}