diff --git a/api/v1alpha1/promise_types.go b/api/v1alpha1/promise_types.go index b4abd973..5deb47ac 100644 --- a/api/v1alpha1/promise_types.go +++ b/api/v1alpha1/promise_types.go @@ -118,6 +118,7 @@ type PromiseStatus struct { Status string `json:"status,omitempty"` RequiredPromises []RequiredPromiseStatus `json:"requiredPromises,omitempty"` RequiredBy []RequiredBy `json:"requiredBy,omitempty"` + LastAvailableTime *metav1.Time `json:"lastAvailableTime,omitempty"` } type PromiseSummary struct { @@ -252,6 +253,15 @@ func (d Dependencies) Marshal() ([]byte, error) { return io.ReadAll(buf) } +func (p *Promise) GetCondition(conditionType string) *metav1.Condition { + for i := range p.Status.Conditions { + if p.Status.Conditions[i].Type == conditionType { + return &p.Status.Conditions[i] + } + } + return nil +} + //+kubebuilder:object:root=true // PromiseList contains a list of Promise diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 25d4638d..1da54e08 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -758,6 +758,10 @@ func (in *PromiseStatus) DeepCopyInto(out *PromiseStatus) { *out = make([]RequiredBy, len(*in)) copy(*out, *in) } + if in.LastAvailableTime != nil { + in, out := &in.LastAvailableTime, &out.LastAvailableTime + *out = (*in).DeepCopy() + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PromiseStatus. diff --git a/config/crd/bases/platform.kratix.io_promises.yaml b/config/crd/bases/platform.kratix.io_promises.yaml index 213743a6..ba7dea88 100644 --- a/config/crd/bases/platform.kratix.io_promises.yaml +++ b/config/crd/bases/platform.kratix.io_promises.yaml @@ -192,6 +192,9 @@ spec: type: array kind: type: string + lastAvailableTime: + format: date-time + type: string observedGeneration: format: int64 type: integer diff --git a/controllers/assets/promise-with-workflow.yaml b/controllers/assets/promise-with-workflow.yaml index 315904fd..e48c3008 100644 --- a/controllers/assets/promise-with-workflow.yaml +++ b/controllers/assets/promise-with-workflow.yaml @@ -2,6 +2,8 @@ apiVersion: platform.kratix.io/v1alpha1 kind: Promise metadata: name: promise-with-workflow + labels: + kratix.io/promise-version: v1.1.0 spec: api: apiVersion: apiextensions.k8s.io/v1 diff --git a/controllers/promise_controller.go b/controllers/promise_controller.go index 7b4d38e9..8b45ac3e 100644 --- a/controllers/promise_controller.go +++ b/controllers/promise_controller.go @@ -21,6 +21,7 @@ import ( "fmt" "slices" "strings" + "sync" "time" "github.com/syntasso/kratix/lib/objectutil" @@ -69,6 +70,10 @@ type PromiseReconciler struct { StartedDynamicControllers map[string]*DynamicResourceRequestController RestartManager func() NumberOfJobsToKeep int + + ScheduledReconciliation map[string]metav1.Time + + mutex sync.Mutex } const ( @@ -77,6 +82,7 @@ const ( dynamicControllerDependantResourcesCleanupFinalizer = v1alpha1.KratixPrefix + "dynamic-controller-dependant-resources-cleanup" crdCleanupFinalizer = v1alpha1.KratixPrefix + "api-crd-cleanup" dependenciesCleanupFinalizer = v1alpha1.KratixPrefix + "dependencies-cleanup" + lastUpdatedAtAnnotation = v1alpha1.KratixPrefix + "last-updated-at" requirementStateInstalled = "Requirement installed" requirementStateNotInstalled = "Requirement not installed" @@ -95,9 +101,9 @@ var ( // fastRequeue can be used whenever we want to quickly requeue, and we don't expect // an error to occur. Example: we delete a resource, we then requeue // to check it's been deleted. Here we can use a fastRequeue instead of a defaultRequeue - fastRequeue = ctrl.Result{RequeueAfter: 1 * time.Second} - defaultRequeue = ctrl.Result{RequeueAfter: 5 * time.Second} - slowRequeue = ctrl.Result{RequeueAfter: 15 * time.Second} + fastRequeue = ctrl.Result{RequeueAfter: 5 * time.Second} + defaultRequeue = ctrl.Result{RequeueAfter: 15 * time.Second} + slowRequeue = ctrl.Result{RequeueAfter: 60 * time.Second} ) //+kubebuilder:rbac:groups=platform.kratix.io,resources=promises,verbs=get;list;watch;create;update;patch;delete @@ -152,9 +158,16 @@ func (r *PromiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct //Set status to unavailable, at the end of this function we set it to //available. If at anytime we return early, it persisted as unavailable promise.Status.Status = v1alpha1.PromiseStatusUnavailable - updated, err := r.ensureRequiredPromiseStatusIsUpToDate(ctx, promise) - if err != nil || updated { - return ctrl.Result{}, err + requirementsChanged := r.hasPromiseRequirementsChanged(ctx, promise) + + scheduledReconciliation := promise.Status.LastAvailableTime != nil && time.Since(promise.Status.LastAvailableTime.Time) > DefaultReconciliationInterval + if (requirementsChanged || scheduledReconciliation) && originalStatus == v1alpha1.PromiseStatusAvailable { + err := r.Client.Status().Update(ctx, promise) + if err != nil { + return ctrl.Result{}, err + } + logger.Info("Requeueing: requirements changed or scheduled reconciliation") + return ctrl.Result{}, nil } //TODO handle removing finalizer @@ -204,13 +217,14 @@ func (r *PromiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return addFinalizers(opts, promise, []string{dependenciesCleanupFinalizer}) } - requeue, err = r.reconcileDependenciesAndPromiseWorkflows(opts, promise) + ctrlResult, err := r.reconcileDependenciesAndPromiseWorkflows(opts, promise) if err != nil { return ctrl.Result{}, err } - if requeue != nil { - return *requeue, nil + if ctrlResult != nil { + logger.Info("stopping reconciliation while reconciling dependencies") + return *ctrlResult, nil } if promise.ContainsAPI() { @@ -240,10 +254,12 @@ func (r *PromiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if promise.GetGeneration() != promise.Status.ObservedGeneration { if promise.GetGeneration() != 1 { + logger.Info("reconciling all RRs") if err := r.reconcileAllRRs(rrGVK); err != nil { return ctrl.Result{}, err } } + logger.Info("updating observed generation", "from", promise.Status.ObservedGeneration, "to", promise.GetGeneration()) promise.Status.ObservedGeneration = promise.GetGeneration() return ctrl.Result{}, r.Client.Status().Update(ctx, promise) } @@ -252,24 +268,38 @@ func (r *PromiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } if originalStatus == v1alpha1.PromiseStatusAvailable { - return ctrl.Result{}, nil + return r.nextReconciliation(promise, logger) } + logger.Info("Promise status being set to Available") promise.Status.Status = v1alpha1.PromiseStatusAvailable + promise.Status.LastAvailableTime = &metav1.Time{Time: time.Now()} return ctrl.Result{}, r.Client.Status().Update(ctx, promise) } -func (r *PromiseReconciler) ensureRequiredPromiseStatusIsUpToDate(ctx context.Context, promise *v1alpha1.Promise) (bool, error) { +func (r *PromiseReconciler) nextReconciliation(promise *v1alpha1.Promise, logger logr.Logger) (ctrl.Result, error) { + r.mutex.Lock() + defer r.mutex.Unlock() + + scheduled, found := r.ScheduledReconciliation[promise.GetName()] + if !found || time.Now().After(scheduled.Time) { + next := metav1.NewTime(time.Now().Add(DefaultReconciliationInterval)) + r.ScheduledReconciliation[promise.GetName()] = next + + logger.Info("Scheduling next reconciliation", "scheduledReconciliationTimestamp", next.Time.String()) + return ctrl.Result{RequeueAfter: DefaultReconciliationInterval}, nil + } + logger.Info("Reconciliation already scheduled", "scheduledReconciliationTimestamp", scheduled.Time.String(), "labels", promise.Labels) + return ctrl.Result{}, nil +} + +func (r *PromiseReconciler) hasPromiseRequirementsChanged(ctx context.Context, promise *v1alpha1.Promise) bool { latestCondition, latestRequirements := r.generateStatusAndMarkRequirements(ctx, promise) requirementsFieldChanged := updateRequirementsStatusOnPromise(promise, promise.Status.RequiredPromises, latestRequirements) conditionsFieldChanged := updateConditionOnPromise(promise, latestCondition) - if conditionsFieldChanged || requirementsFieldChanged { - return true, r.Client.Status().Update(ctx, promise) - } - - return false, nil + return conditionsFieldChanged || requirementsFieldChanged } func updateConditionOnPromise(promise *v1alpha1.Promise, latestCondition metav1.Condition) bool { @@ -377,6 +407,19 @@ func (r *PromiseReconciler) reconcileDependenciesAndPromiseWorkflows(o opts, pro } o.logger.Info("Promise contains workflows.promise.configure, reconciling workflows") + pipelineCompletedCondition := promise.GetCondition(string(resourceutil.PipelineCompletedCondition)) + forcePipelineRun := pipelineCompletedCondition != nil && pipelineCompletedCondition.Status == "True" && time.Since(pipelineCompletedCondition.LastTransitionTime.Time) > DefaultReconciliationInterval + if forcePipelineRun { + o.logger.Info("Pipeline completed too long ago... forcing the reconciliation", "lastTransitionTime", pipelineCompletedCondition.LastTransitionTime.Time.String()) + if promise.Labels == nil { + promise.Labels = make(map[string]string) + } + promise.Labels[resourceutil.ManualReconciliationLabel] = "true" + if err := r.Client.Update(o.ctx, promise); err != nil { + return &ctrl.Result{}, err + } + } + unstructuredPromise, err := promise.ToUnstructured() if err != nil { return nil, err @@ -977,6 +1020,14 @@ func (r *PromiseReconciler) applyWorkForStaticDependencies(o opts, promise *v1al } else { op = "updated" existingWork.Spec = work.Spec + + ann := existingWork.GetAnnotations() + if ann == nil { + ann = map[string]string{} + } + ann[lastUpdatedAtAnnotation] = time.Now().Local().String() + existingWork.SetAnnotations(ann) + err = r.Client.Update(o.ctx, existingWork) } diff --git a/controllers/promise_controller_test.go b/controllers/promise_controller_test.go index a023dfbe..05c0ded5 100644 --- a/controllers/promise_controller_test.go +++ b/controllers/promise_controller_test.go @@ -7,6 +7,7 @@ import ( "os" "regexp" "strings" + "time" "github.com/go-logr/logr" . "github.com/onsi/ginkgo/v2" @@ -28,7 +29,9 @@ import ( "github.com/syntasso/kratix/controllers" "github.com/syntasso/kratix/controllers/controllersfakes" "github.com/syntasso/kratix/lib/compression" + "github.com/syntasso/kratix/lib/resourceutil" "github.com/syntasso/kratix/lib/workflow" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" ) var ( @@ -54,10 +57,11 @@ var _ = Describe("PromiseController", func() { managerRestarted = false l = ctrl.Log.WithName("controllers").WithName("Promise") reconciler = &controllers.PromiseReconciler{ - Client: fakeK8sClient, - ApiextensionsClient: fakeApiExtensionsClient, - Log: l, - Manager: &controllersfakes.FakeManager{}, + Client: fakeK8sClient, + ApiextensionsClient: fakeApiExtensionsClient, + Log: l, + Manager: &controllersfakes.FakeManager{}, + ScheduledReconciliation: map[string]metav1.Time{}, RestartManager: func() { managerRestarted = true }, @@ -77,7 +81,7 @@ var _ = Describe("PromiseController", func() { }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) By("creating the CRD", func() { crd, err := fakeApiExtensionsClient.CustomResourceDefinitions().Get(ctx, expectedCRDName, metav1.GetOptions{}) @@ -182,6 +186,7 @@ var _ = Describe("PromiseController", func() { work := getWork("kratix-platform-system", promise.GetName(), "", "") Expect(inCompressedContents(work.Spec.WorkloadGroups[0].Workloads[0].Content, []byte("kind: Deployment"))).To(BeTrue()) Expect(inCompressedContents(work.Spec.WorkloadGroups[0].Workloads[0].Content, []byte("kind: ClusterRoleBinding"))).To(BeTrue()) + Expect(work.GetAnnotations()).To(HaveKey("kratix.io/last-updated-at")) }) @@ -463,7 +468,7 @@ var _ = Describe("PromiseController", func() { result, err := t.reconcileUntilCompletion(reconciler, promise) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) }) By("not creating a Work for the empty static dependencies", func() { @@ -498,7 +503,7 @@ var _ = Describe("PromiseController", func() { It("re-reconciles until completion", func() { result, err := t.reconcileUntilCompletion(reconciler, promise) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) crds, err := fakeApiExtensionsClient.CustomResourceDefinitions().List(ctx, metav1.ListOptions{}) Expect(err).NotTo(HaveOccurred()) @@ -546,7 +551,7 @@ var _ = Describe("PromiseController", func() { }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) }) It("sets the finalizers on the Promise", func() { @@ -658,7 +663,7 @@ var _ = Describe("PromiseController", func() { }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) }) It("sets the delete-workflows finalizer", func() { @@ -752,7 +757,7 @@ var _ = Describe("PromiseController", func() { funcs: []func(client.Object) error{autoMarkCRDAsEstablished}, }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) }) When("it contains static dependencies", func() { @@ -809,6 +814,104 @@ var _ = Describe("PromiseController", func() { }) }) }) + + When("the reconciliation interval is reached", func() { + BeforeEach(func() { + promise = createPromise(promiseWithWorkflowPath) + setReconcileConfigureWorkflowToReturnFinished() + + result, err := t.reconcileUntilCompletion(reconciler, promise, &opts{ + funcs: []func(client.Object) error{autoMarkCRDAsEstablished}, + }) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) + + Expect(fakeK8sClient.Get(ctx, types.NamespacedName{Name: promise.GetName()}, promise)).To(Succeed()) + + uPromise, err := promise.ToUnstructured() + Expect(err).NotTo(HaveOccurred()) + + resourceutil.SetCondition(uPromise, &clusterv1.Condition{ + Type: resourceutil.PipelineCompletedCondition, + Status: v1.ConditionTrue, + Message: "Pipeline completed", + Reason: "PipelineExecutedSuccessfully", + LastTransitionTime: metav1.NewTime(time.Now().Add(-controllers.DefaultReconciliationInterval)), + }) + resourceutil.MarkPipelineAsCompleted(logr.Logger{}, uPromise) + Expect(fakeK8sClient.Status().Update(ctx, uPromise)).To(Succeed()) + }) + + It("re-runs the promise.configure workflow and sets the next reconciliation timestamp", func() { + Expect(fakeK8sClient.Get(ctx, promiseName, promise)).To(Succeed()) + tm := metav1.NewTime(time.Now().Add(-controllers.DefaultReconciliationInterval)) + promise.Status.LastAvailableTime = &tm + Expect(fakeK8sClient.Status().Update(ctx, promise)).To(Succeed()) + + result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: promise.GetName(), Namespace: promise.GetNamespace()}}) + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(ctrl.Result{})) + + Expect(fakeK8sClient.Get(ctx, promiseName, promise)).To(Succeed()) + Expect(promise.Status.Status).To(Equal(v1alpha1.PromiseStatusUnavailable)) + + By("Adding the manual reconciliation label", func() { + result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: promise.GetName(), Namespace: promise.GetNamespace()}}) + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(ctrl.Result{})) + + Expect(fakeK8sClient.Get(ctx, promiseName, promise)).To(Succeed()) + Expect(promise.Labels[resourceutil.ManualReconciliationLabel]).To(Equal("true")) + }) + + By("running the pipelines", func() { + Expect(fakeK8sClient.Get(ctx, types.NamespacedName{Name: promise.GetName()}, promise)).To(Succeed()) + + uPromise, err := promise.ToUnstructured() + Expect(err).NotTo(HaveOccurred()) + + resourceutil.MarkPipelineAsRunning(logr.Logger{}, uPromise) + Expect(fakeK8sClient.Status().Update(ctx, uPromise)).To(Succeed()) + + result, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: promise.GetName(), Namespace: promise.GetNamespace()}}) + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal(ctrl.Result{})) + }) + + By("completing the pipelines", func() { + Expect(fakeK8sClient.Get(ctx, types.NamespacedName{Name: promise.GetName()}, promise)).To(Succeed()) + + uPromise, err := promise.ToUnstructured() + Expect(err).NotTo(HaveOccurred()) + + resourceutil.SetCondition(uPromise, &clusterv1.Condition{ + Type: resourceutil.PipelineCompletedCondition, + Status: v1.ConditionTrue, + Message: "Pipeline completed", + Reason: "PipelineExecutedSuccessfully", + LastTransitionTime: metav1.NewTime(time.Now()), + }) + resourceutil.MarkPipelineAsCompleted(logr.Logger{}, uPromise) + Expect(fakeK8sClient.Status().Update(ctx, uPromise)).To(Succeed()) + + Expect(fakeK8sClient.Get(ctx, promiseName, promise)).To(Succeed()) + Expect(promise.Status.Status).To(Equal(v1alpha1.PromiseStatusAvailable)) + }) + + By("setting up the next reconciliation loop", func() { + result, err := t.reconcileUntilCompletion(reconciler, promise, &opts{ + funcs: []func(client.Object) error{autoMarkCRDAsEstablished}, + }) + + Expect(fakeK8sClient.Get(ctx, types.NamespacedName{Name: promise.GetName()}, promise)).To(Succeed()) + + Expect(err).NotTo(HaveOccurred()) + Expect(result).To(Equal(ctrl.Result{})) + Expect(reconciler.ScheduledReconciliation[promise.GetName()].Unix()).To(Equal(time.Now().Add(controllers.DefaultReconciliationInterval).Unix())) + }) + }) + }) }) Describe("Promise API", func() { @@ -820,7 +923,7 @@ var _ = Describe("PromiseController", func() { funcs: []func(client.Object) error{autoMarkCRDAsEstablished}, }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) crd, err := fakeApiExtensionsClient.CustomResourceDefinitions().Get(ctx, expectedCRDName, metav1.GetOptions{}) Expect(err).ToNot(HaveOccurred()) @@ -857,7 +960,7 @@ var _ = Describe("PromiseController", func() { }) Expect(err).NotTo(HaveOccurred()) - Expect(result).To(Equal(ctrl.Result{})) + Expect(result).To(Equal(ctrl.Result{RequeueAfter: controllers.DefaultReconciliationInterval})) crd, err := fakeApiExtensionsClient.CustomResourceDefinitions().Get(ctx, expectedCRDName, metav1.GetOptions{}) Expect(err).ToNot(HaveOccurred()) @@ -909,6 +1012,8 @@ func getWork(namespace, promiseName, resourceName, pipelineName string) v1alpha1 workSelectorLabel := labels.FormatLabels(l) selector, err := labels.Parse(workSelectorLabel) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + err = fakeK8sClient.List(context.Background(), &works, &client.ListOptions{ LabelSelector: selector, Namespace: namespace, diff --git a/controllers/scheduler.go b/controllers/scheduler.go index 68a52f32..ad59bff7 100644 --- a/controllers/scheduler.go +++ b/controllers/scheduler.go @@ -301,6 +301,8 @@ func (s *Scheduler) applyWorkplacementsForTargetDestinations(workloadGroup v1alp workLabelKey: work.Name, workloadGroupIDKey: workloadGroup.ID, } + workPlacement.SetAnnotations(work.GetAnnotations()) + workPlacement.SetPipelineName(work) if misscheduled { diff --git a/controllers/scheduler_test.go b/controllers/scheduler_test.go index cf4c2d14..a91f1d44 100644 --- a/controllers/scheduler_test.go +++ b/controllers/scheduler_test.go @@ -86,6 +86,7 @@ var _ = Describe("Controllers/Scheduler", func() { Expect(workPlacement.GetLabels()).To(SatisfyAll( HaveKeyWithValue("kratix.io/pipeline-name", resourceWork.Labels["kratix.io/pipeline-name"]), )) + Expect(workPlacement.GetAnnotations()).To(Equal(resourceWork.GetAnnotations())) }) It("sets the scheduling conditions on the Work", func() { @@ -138,6 +139,7 @@ var _ = Describe("Controllers/Scheduler", func() { Expect(newWorkPlacement.Finalizers).To(Equal(workPlacement.Finalizers)) Expect(newWorkPlacement.Spec.PromiseName).To(Equal(workPlacement.Spec.PromiseName)) Expect(newWorkPlacement.Spec.ResourceName).To(Equal(workPlacement.Spec.ResourceName)) + Expect(newWorkPlacement.GetAnnotations()).To(Equal(workPlacement.GetAnnotations())) }) }) @@ -345,6 +347,7 @@ var _ = Describe("Controllers/Scheduler", func() { Expect(devOrProdWorkPlacement.Finalizers[0]).To(Equal("finalizers.workplacement.kratix.io/repo-cleanup")) Expect(devOrProdWorkPlacement.Spec.PromiseName).To(Equal("promise")) Expect(devOrProdWorkPlacement.Spec.ResourceName).To(Equal("resource")) + Expect(devOrProdWorkPlacement.GetAnnotations()).To(Equal(resourceWorkWithMultipleGroups.GetAnnotations())) Expect(pciWorkPlacement.Namespace).To(Equal("default")) Expect(pciWorkPlacement.ObjectMeta.Labels["kratix.io/work"]).To(Equal("rr-work-name-with-two-groups")) @@ -356,6 +359,8 @@ var _ = Describe("Controllers/Scheduler", func() { Expect(pciWorkPlacement.Finalizers[0]).To(Equal("finalizers.workplacement.kratix.io/repo-cleanup")) Expect(pciWorkPlacement.Spec.PromiseName).To(Equal("promise")) Expect(pciWorkPlacement.Spec.ResourceName).To(Equal("resource")) + Expect(pciWorkPlacement.Spec.ResourceName).To(Equal("resource")) + Expect(pciWorkPlacement.GetAnnotations()).To(Equal(resourceWorkWithMultipleGroups.GetAnnotations())) }) }) @@ -434,6 +439,7 @@ var _ = Describe("Controllers/Scheduler", func() { Expect(pciWorkPlacement.Spec.Workloads).To(Equal(resourceWorkWithMultipleGroups.Spec.WorkloadGroups[1].Workloads)) Expect(pciWorkPlacement.Spec.PromiseName).To(Equal("promise")) Expect(pciWorkPlacement.Spec.ResourceName).To(Equal("resource")) + Expect(pciWorkPlacement.GetAnnotations()).To(Equal(resourceWorkWithMultipleGroups.GetAnnotations())) }) It("returns an error indicating what was unschedulable", func() { @@ -554,6 +560,7 @@ var _ = Describe("Controllers/Scheduler", func() { HaveKeyWithValue("kratix.io/pipeline-name", dependencyWorkForProd.Labels["kratix.io/pipeline-name"]), HaveKeyWithValue("kratix.io/work", dependencyWorkForProd.Name), )) + Expect(workPlacements.Items[0].GetAnnotations()).To(Equal(dependencyWorkForProd.GetAnnotations())) }) }) @@ -855,6 +862,9 @@ func newWork(name string, isResource bool, scheduling ...WorkloadGroupScheduling "kratix.io/work": name, "kratix.io/pipeline-name": fmt.Sprintf("workflow-%s", uuid.New().String()[0:8]), }, + Annotations: map[string]string{ + "kratix.io/some-annotation": "some-value", + }, }, Spec: WorkSpec{ PromiseName: "promise", @@ -898,6 +908,9 @@ func newWorkWithTwoWorkloadGroups(name string, isResource bool, promiseSchedulin ObjectMeta: v1.ObjectMeta{ Name: name, Namespace: namespace, + Annotations: map[string]string{ + "kratix.io/annotation-key": "annotation-value", + }, }, Spec: WorkSpec{ PromiseName: "promise", diff --git a/controllers/shared.go b/controllers/shared.go index e55ce30e..40931911 100644 --- a/controllers/shared.go +++ b/controllers/shared.go @@ -3,6 +3,7 @@ package controllers import ( "context" "fmt" + "time" "github.com/go-logr/logr" "github.com/syntasso/kratix/api/v1alpha1" @@ -24,6 +25,7 @@ const ( promiseReleaseNameLabel = v1alpha1.KratixPrefix + "promise-release-name" removeAllWorkflowJobsFinalizer = v1alpha1.KratixPrefix + "workflows-cleanup" runDeleteWorkflowsFinalizer = v1alpha1.KratixPrefix + "delete-workflows" + DefaultReconciliationInterval = time.Hour * 10 ) var ( diff --git a/controllers/shared_test.go b/controllers/shared_test.go index 157d0146..e097dfed 100644 --- a/controllers/shared_test.go +++ b/controllers/shared_test.go @@ -8,6 +8,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/syntasso/kratix/api/v1alpha1" + "github.com/syntasso/kratix/controllers" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" @@ -75,7 +76,7 @@ type testReconciler struct { } // Run the reconciler until all these are satisfied: -// - reconciler is not returning a requeuing result and no error +// - reconciler is not returning a requeuing result and no error (e.g. `ctrl.Result{}, nil`) // - the resource is not updated after a reconcile // TODO: We watch for various other resources to trigger reconciliaton loop, // e.g. changes to jobs owned by a promise trigger the promise. Need to improve @@ -132,14 +133,14 @@ func (t *testReconciler) reconcileUntilCompletion(r kubebuilder.Reconciler, obj err = fakeK8sClient.Get(context.Background(), namespacedName, newK8sObj) if err != nil { if errors.IsNotFound(err) { - // The object was deleted, so we need to requeue one last time to mimick - // the k8s api behaviou + // The object was deleted, so we need to requeue one last time to mimic + // the k8s api behaviour return r.Reconcile(context.Background(), ctrl.Request{NamespacedName: namespacedName}) } return ctrl.Result{}, err } - if int64(result.RequeueAfter) == 0 && k8sObj.GetResourceVersion() == newK8sObj.GetResourceVersion() { + if (int64(result.RequeueAfter) == 0 || result.RequeueAfter == controllers.DefaultReconciliationInterval) && k8sObj.GetResourceVersion() == newK8sObj.GetResourceVersion() { return result, nil } diff --git a/go.mod b/go.mod index eb7fe0bd..0e38710f 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,15 @@ go 1.22.5 require ( github.com/go-git/go-git/v5 v5.11.0 - github.com/go-logr/logr v1.4.1 + github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 github.com/minio/minio-go/v7 v7.0.68 - github.com/onsi/ginkgo/v2 v2.17.2 - github.com/onsi/gomega v1.33.1 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.1 github.com/pkg/errors v0.9.1 go.uber.org/zap v1.26.0 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.30.1 k8s.io/apiextensions-apiserver v0.30.1 @@ -54,7 +54,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -80,19 +80,19 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 55d9e288..8e397a68 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3c github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -81,6 +83,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -129,8 +133,12 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -189,14 +197,20 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -210,6 +224,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -219,6 +235,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -236,6 +254,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -243,6 +263,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -253,6 +275,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -263,6 +287,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -273,6 +299,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/main.go b/main.go index 70ef3af6..33b184f9 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/go-logr/logr" "github.com/syntasso/kratix/api/v1alpha1" @@ -140,12 +141,13 @@ func main() { restartManager := false restartManagerInProgress := false if err = (&controllers.PromiseReconciler{ - ApiextensionsClient: apiextensionsClient.ApiextensionsV1(), - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("Promise"), - Manager: mgr, - Scheme: mgr.GetScheme(), - NumberOfJobsToKeep: getNumJobsToKeep(kratixConfig), + ApiextensionsClient: apiextensionsClient.ApiextensionsV1(), + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("Promise"), + Manager: mgr, + Scheme: mgr.GetScheme(), + NumberOfJobsToKeep: getNumJobsToKeep(kratixConfig), + ScheduledReconciliation: map[string]metav1.Time{}, RestartManager: func() { // This function gets called multiple times // First call: restartInProgress get set to true, sleeps starts