diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 17518a7b..3a29e935 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -12,6 +12,6 @@ jobs: uses: CircleCI-Public/trigger-circleci-pipeline-action@v1.1.0 with: GHA_Meta: ${{ github.event.release.tag_name }} - target-slug: ${{ secrets.CIRCLECI_TARGET_SLUG }} + target-slug: ${{ secrets.CCI_TARGET_SLUG }} env: CCI_TOKEN: ${{ secrets.CCI_TOKEN }} \ No newline at end of file diff --git a/PROJECT b/PROJECT index 28f6f64f..b77f59f8 100644 --- a/PROJECT +++ b/PROJECT @@ -83,4 +83,7 @@ resources: kind: PromiseRelease path: github.com/syntasso/kratix/api/v1alpha1 version: v1alpha1 + webhooks: + validation: true + webhookVersion: v1 version: "3" diff --git a/api/v1alpha1/export_test.go b/api/v1alpha1/export_test.go index 3e4f319b..d9f0d440 100644 --- a/api/v1alpha1/export_test.go +++ b/api/v1alpha1/export_test.go @@ -12,3 +12,7 @@ func SetClientSet(cs clientset.Interface) { func SetClient(c client.Client) { k8sClient = c } + +func SetPromiseFetcher(pf PromiseFetcher) { + promiseFetcher = pf +} diff --git a/api/v1alpha1/interface.go b/api/v1alpha1/interface.go new file mode 100644 index 00000000..8dbed412 --- /dev/null +++ b/api/v1alpha1/interface.go @@ -0,0 +1,8 @@ +package v1alpha1 + +// +kubebuilder:object:generate=false +// +//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . PromiseFetcher +type PromiseFetcher interface { + FromURL(string) (*Promise, error) +} diff --git a/api/v1alpha1/promise_webhook.go b/api/v1alpha1/promise_webhook.go index e886ff10..1ae4e7af 100644 --- a/api/v1alpha1/promise_webhook.go +++ b/api/v1alpha1/promise_webhook.go @@ -48,8 +48,6 @@ func (p *Promise) SetupWebhookWithManager(mgr ctrl.Manager, cs *clientset.Client Complete() } -//+kubebuilder:webhook:path=/mutate-platform-kratix-io-v1alpha1-promise,mutating=true,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=mpromise.kb.io,admissionReviewVersions=v1 - var _ webhook.Defaulter = &Promise{} // Default implements webhook.Defaulter so a webhook will be registered for the type @@ -57,8 +55,7 @@ func (p *Promise) Default() { promiselog.Info("default", "name", p.Name) } -//+kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promise,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=vpromise.kb.io,admissionReviewVersions=v1 - +// +kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promise,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=vpromise.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &Promise{} func (p *Promise) validateCRD() error { diff --git a/api/v1alpha1/promiserelease_types.go b/api/v1alpha1/promiserelease_types.go index f331d10c..7186687f 100644 --- a/api/v1alpha1/promiserelease_types.go +++ b/api/v1alpha1/promiserelease_types.go @@ -20,6 +20,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + TypeHTTP = "http" +) + // PromiseReleaseSpec defines the desired state of PromiseRelease type PromiseReleaseSpec struct { Version string `json:"version,omitempty"` @@ -33,13 +37,14 @@ type SourceRef struct { // PromiseReleaseStatus defines the observed state of PromiseRelease type PromiseReleaseStatus struct { - Installed bool `json:"installed,omitempty"` + Status string `json:"status,omitempty"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status //+kubebuilder:resource:scope=Cluster,path=promisereleases -//+kubebuilder:printcolumn:JSONPath=".status.installed",name="Installed",type=boolean +//+kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string //+kubebuilder:printcolumn:JSONPath=".spec.version",name="Version",type=string // PromiseRelease is the Schema for the promisereleases API diff --git a/api/v1alpha1/promiserelease_webhook.go b/api/v1alpha1/promiserelease_webhook.go new file mode 100644 index 00000000..315f1b81 --- /dev/null +++ b/api/v1alpha1/promiserelease_webhook.go @@ -0,0 +1,99 @@ +/* +Copyright 2021 Syntasso. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +const ( + KratixPrefix = "kratix.io/" + PromiseVersionLabel = KratixPrefix + "promise-version" +) + +var ( + promiseFetcher PromiseFetcher + promisereleaselog = logf.Log.WithName("promiserelease-resource") +) + +func (r *PromiseRelease) SetupWebhookWithManager(mgr ctrl.Manager, pf PromiseFetcher) error { + promiseFetcher = pf + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// +kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promiserelease,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promisereleases,verbs=create;update,versions=v1alpha1,name=vpromiserelease.kb.io,admissionReviewVersions=v1 +var _ webhook.Validator = &PromiseRelease{} + +func (r *PromiseRelease) ValidateCreate() (admission.Warnings, error) { + promisereleaselog.Info("validate create", "name", r.Name) + if err := r.validate(); err != nil { + return nil, err + } + + promise, err := promiseFetcher.FromURL(r.Spec.SourceRef.URL) + if err != nil { + return nil, fmt.Errorf("failed to fetch promise: %w", err) + } + + promiseVersion, found := promise.GetLabels()[PromiseVersionLabel] + if !found { + msg := fmt.Sprintf("Warning: version label (%s) not found on promise, installation will fail", PromiseVersionLabel) + return []string{msg}, nil + } + + if promiseVersion != r.Spec.Version { + msg := fmt.Sprintf("Warning: version labels do not match, found: %s, expected: %s, installation will fail", promiseVersion, r.Spec.Version) + return []string{msg}, nil + } + + return nil, nil +} + +func (r *PromiseRelease) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + promisereleaselog.Info("validate update", "name", r.Name) + // oldPromiseRelease, _ := old.(*PromiseRelease) + if err := r.validate(); err != nil { + return nil, err + } + + return nil, nil +} + +func (r *PromiseRelease) validate() error { + if r.Spec.SourceRef.Type != TypeHTTP { + return fmt.Errorf("unknown sourceRef type %q", r.Spec.SourceRef.Type) + } + + if r.Spec.SourceRef.URL == "" { + return fmt.Errorf("sourceRef.url must be set") + } + + return nil +} + +func (r *PromiseRelease) ValidateDelete() (admission.Warnings, error) { + // promisereleaselog.Info("validate delete", "name", r.Name) + return nil, nil +} diff --git a/api/v1alpha1/promiserelease_webhook_test.go b/api/v1alpha1/promiserelease_webhook_test.go new file mode 100644 index 00000000..d55d0fae --- /dev/null +++ b/api/v1alpha1/promiserelease_webhook_test.go @@ -0,0 +1,116 @@ +package v1alpha1_test + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/syntasso/kratix/api/v1alpha1" + "github.com/syntasso/kratix/api/v1alpha1/v1alpha1fakes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var _ = Describe("PromiseReleaseWebhook", func() { + var ( + p *v1alpha1.Promise + pr *v1alpha1.PromiseRelease + promiseFetcher v1alpha1fakes.FakePromiseFetcher + ) + BeforeEach(func() { + promiseFetcher = v1alpha1fakes.FakePromiseFetcher{} + v1alpha1.SetPromiseFetcher(&promiseFetcher) + + pr = &v1alpha1.PromiseRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mycrds.group.example", + Namespace: "default", + }, + Spec: v1alpha1.PromiseReleaseSpec{ + Version: "v0.1.0", + SourceRef: v1alpha1.SourceRef{ + Type: "http", + URL: "example.com", + }, + }, + } + + p = &v1alpha1.Promise{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Labels: map[string]string{ + "kratix.io/promise-version": "v0.1.0", + }, + }, + } + }) + + When("source ref is unknown", func() { + It("errors on create and update", func() { + pr.Spec.SourceRef.Type = "ssh" + warnings, err := pr.ValidateCreate() + Expect(warnings).To(BeEmpty()) + Expect(err).To(MatchError("unknown sourceRef type \"ssh\"")) + + warnings, err = pr.ValidateUpdate(pr) + Expect(warnings).To(BeEmpty()) + Expect(err).To(MatchError("unknown sourceRef type \"ssh\"")) + }) + }) + + When("URL is empty", func() { + It("errors on create and update", func() { + pr.Spec.SourceRef.URL = "" + warnings, err := pr.ValidateCreate() + Expect(warnings).To(BeEmpty()) + Expect(err).To(MatchError("sourceRef.url must be set")) + + warnings, err = pr.ValidateUpdate(pr) + Expect(warnings).To(BeEmpty()) + Expect(err).To(MatchError("sourceRef.url must be set")) + }) + }) + + When("fetching the URL fails", func() { + It("errors on create", func() { + promiseFetcher.FromURLReturns(p, fmt.Errorf("foo")) + warnings, err := pr.ValidateCreate() + Expect(warnings).To(BeEmpty()) + Expect(err).To(MatchError("failed to fetch promise: foo")) + + warnings, err = pr.ValidateUpdate(pr) + Expect(warnings).To(BeEmpty()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("does not error on update", func() { + //We only want to fetch it on create, its expensive to do this call + //frequently. + promiseFetcher.FromURLReturns(p, fmt.Errorf("foo")) + warnings, err := pr.ValidateUpdate(pr) + Expect(warnings).To(BeEmpty()) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + When("the promise is missing the label", func() { + It("emits a warning", func() { + p.Labels = map[string]string{} + promiseFetcher.FromURLReturns(p, nil) + warnings, err := pr.ValidateCreate() + Expect(err).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("Warning: version label (kratix.io/promise-version) not found on promise, installation will fail")) + }) + }) + + When("the promise is at a different version", func() { + It("emits a warning", func() { + p.Labels = map[string]string{ + "kratix.io/promise-version": "v0.2.0", + } + promiseFetcher.FromURLReturns(p, nil) + warnings, err := pr.ValidateCreate() + Expect(err).NotTo(HaveOccurred()) + Expect(warnings).To(ConsistOf("Warning: version labels do not match, found: v0.2.0, expected: v0.1.0, installation will fail")) + }) + }) +}) diff --git a/controllers/controllersfakes/fake_promise_fetcher.go b/api/v1alpha1/v1alpha1fakes/fake_promise_fetcher.go similarity index 96% rename from controllers/controllersfakes/fake_promise_fetcher.go rename to api/v1alpha1/v1alpha1fakes/fake_promise_fetcher.go index 3c338bea..23aeb26e 100644 --- a/controllers/controllersfakes/fake_promise_fetcher.go +++ b/api/v1alpha1/v1alpha1fakes/fake_promise_fetcher.go @@ -1,11 +1,10 @@ // Code generated by counterfeiter. DO NOT EDIT. -package controllersfakes +package v1alpha1fakes import ( "sync" "github.com/syntasso/kratix/api/v1alpha1" - "github.com/syntasso/kratix/controllers" ) type FakePromiseFetcher struct { @@ -114,4 +113,4 @@ func (fake *FakePromiseFetcher) recordInvocation(key string, args []interface{}) fake.invocations[key] = append(fake.invocations[key], args) } -var _ controllers.PromiseFetcher = new(FakePromiseFetcher) +var _ v1alpha1.PromiseFetcher = new(FakePromiseFetcher) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 97922f87..43f5e85b 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -540,7 +540,7 @@ func (in *PromiseRelease) DeepCopyInto(out *PromiseRelease) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PromiseRelease. @@ -612,6 +612,13 @@ func (in *PromiseReleaseSpec) DeepCopy() *PromiseReleaseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PromiseReleaseStatus) DeepCopyInto(out *PromiseReleaseStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PromiseReleaseStatus. diff --git a/config/crd/bases/platform.kratix.io_promisereleases.yaml b/config/crd/bases/platform.kratix.io_promisereleases.yaml index 39abee78..b11f33f3 100644 --- a/config/crd/bases/platform.kratix.io_promisereleases.yaml +++ b/config/crd/bases/platform.kratix.io_promisereleases.yaml @@ -15,9 +15,9 @@ spec: scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .status.installed - name: Installed - type: boolean + - jsonPath: .status.status + name: Status + type: string - jsonPath: .spec.version name: Version type: string @@ -54,8 +54,76 @@ spec: status: description: PromiseReleaseStatus defines the observed state of PromiseRelease properties: - installed: - type: boolean + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + status: + type: string type: object type: object served: true diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 6de1bef0..ca3fdf6a 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -50,3 +50,23 @@ webhooks: resources: - promises sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-platform-kratix-io-v1alpha1-promiserelease + failurePolicy: Fail + name: vpromiserelease.kb.io + rules: + - apiGroups: + - platform.kratix.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - promisereleases + sideEffects: None diff --git a/controllers/dynamic_resource_request_controller.go b/controllers/dynamic_resource_request_controller.go index 926902da..ff0b7740 100644 --- a/controllers/dynamic_resource_request_controller.go +++ b/controllers/dynamic_resource_request_controller.go @@ -40,7 +40,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const workFinalizer = kratixPrefix + "work-cleanup" +const ( + workFinalizer = v1alpha1.KratixPrefix + "work-cleanup" + workflowsFinalizer = v1alpha1.KratixPrefix + "workflows-cleanup" + deleteWorkflowsFinalizer = v1alpha1.KratixPrefix + "delete-workflows" +) var rrFinalizers = []string{workFinalizer, removeAllWorkflowJobsFinalizer, runDeleteWorkflowsFinalizer} diff --git a/controllers/promise_controller.go b/controllers/promise_controller.go index c20057e4..88a65059 100644 --- a/controllers/promise_controller.go +++ b/controllers/promise_controller.go @@ -66,11 +66,11 @@ type PromiseReconciler struct { } const ( - resourceRequestCleanupFinalizer = kratixPrefix + "resource-request-cleanup" + resourceRequestCleanupFinalizer = v1alpha1.KratixPrefix + "resource-request-cleanup" // TODO fix the name of this finalizer: dependant -> dependent (breaking change) - dynamicControllerDependantResourcesCleaupFinalizer = kratixPrefix + "dynamic-controller-dependant-resources-cleanup" - crdCleanupFinalizer = kratixPrefix + "api-crd-cleanup" - dependenciesCleanupFinalizer = kratixPrefix + "dependencies-cleanup" + dynamicControllerDependantResourcesCleaupFinalizer = v1alpha1.KratixPrefix + "dynamic-controller-dependant-resources-cleanup" + crdCleanupFinalizer = v1alpha1.KratixPrefix + "api-crd-cleanup" + dependenciesCleanupFinalizer = v1alpha1.KratixPrefix + "dependencies-cleanup" requirementStateInstalled = "Requirement installed" requirementStateNotInstalled = "Requirement not installed" @@ -140,7 +140,7 @@ func (r *PromiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return r.deletePromise(opts, promise, pipelines.DeletePromise) } - if value, found := promise.Labels[promiseVersionLabel]; found { + if value, found := promise.Labels[v1alpha1.PromiseVersionLabel]; found { if promise.Status.Version != value { promise.Status.Version = value return ctrl.Result{}, r.Client.Status().Update(ctx, promise) diff --git a/controllers/promiserelease_controller.go b/controllers/promiserelease_controller.go index e15c39e0..e10db358 100644 --- a/controllers/promiserelease_controller.go +++ b/controllers/promiserelease_controller.go @@ -21,6 +21,7 @@ import ( "fmt" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -35,20 +36,23 @@ import ( "github.com/syntasso/kratix/lib/resourceutil" ) -//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . PromiseFetcher -type PromiseFetcher interface { - FromURL(string) (*v1alpha1.Promise, error) -} +const ( + statusInstalled = "Installed" + statusErrorInstalling = "Error installing" + + conditionMessageInstalled = "Installed successfully" + conditionReasonInstalled = "InstalledSuccessfully" +) // PromiseReleaseReconciler reconciles a PromiseRelease object type PromiseReleaseReconciler struct { client.Client Scheme *runtime.Scheme Log logr.Logger - PromiseFetcher PromiseFetcher + PromiseFetcher v1alpha1.PromiseFetcher } -const promiseCleanupFinalizer = kratixPrefix + "promise-cleanup" +const promiseCleanupFinalizer = v1alpha1.KratixPrefix + "promise-cleanup" //+kubebuilder:rbac:groups=platform.kratix.io,resources=promisereleases,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=platform.kratix.io,resources=promisereleases/status,verbs=get;update;patch @@ -84,16 +88,26 @@ func (r *PromiseReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reque return addFinalizers(opts, promiseRelease, []string{promiseCleanupFinalizer}) } - if promiseRelease.Status.Installed { - return r.reconcileOnInstalledPromise(opts, promiseRelease) + exists, err := r.promiseExistsAtDesiredVersion(opts, promiseRelease) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to check if promise exists: %w", err) + } + + if exists { + logger.Info("Promise exists, skipping install") + r.updateStatusAndConditions(opts, promiseRelease, statusInstalled, conditionMessageInstalled, conditionReasonInstalled) + return ctrl.Result{}, nil } + logger.Info("Promise does not exist, installing") + var promise *v1alpha1.Promise switch sourceRefType := promiseRelease.Spec.SourceRef.Type; sourceRefType { - case "http": + case v1alpha1.TypeHTTP: promise, err = r.PromiseFetcher.FromURL(promiseRelease.Spec.SourceRef.URL) if err != nil { + r.updateStatusAndConditions(opts, promiseRelease, statusErrorInstalling, "Failed to fetch Promise from URL", "FailedToFetchPromise") return ctrl.Result{}, fmt.Errorf("failed to fetch promise from url: %w", err) } updated, err := r.validateVersion(opts, promiseRelease, promise) @@ -101,18 +115,17 @@ func (r *PromiseReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } default: - return ctrl.Result{}, fmt.Errorf("unknown sourceRef type: %s", sourceRefType) + logger.Error(fmt.Errorf("unknown sourceRef type: %s", sourceRefType), "not requeueing") + return ctrl.Result{}, nil } if err := r.installPromise(opts, promiseRelease, promise); err != nil { + r.updateStatusAndConditions(opts, promiseRelease, statusErrorInstalling, "Failed to create or update Promise", "FailedToCreateOrUpdatePromise") return ctrl.Result{}, fmt.Errorf("failed to create or update promise: %w", err) } - promiseRelease.Status.Installed = true - if err := r.Client.Status().Update(ctx, promiseRelease); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to update PromiseRelease status: %w", err) - } - + promiseRelease.Status.Status = statusInstalled + r.updateStatusAndConditions(opts, promiseRelease, statusInstalled, conditionMessageInstalled, conditionReasonInstalled) return ctrl.Result{}, nil } @@ -196,43 +209,62 @@ func (r *PromiseReleaseReconciler) delete(o opts, promiseRelease *v1alpha1.Promi return defaultRequeue, nil } -func (r *PromiseReleaseReconciler) reconcileOnInstalledPromise(o opts, promiseRelease *v1alpha1.PromiseRelease) (ctrl.Result, error) { +func (r *PromiseReleaseReconciler) promiseExistsAtDesiredVersion(o opts, promiseRelease *v1alpha1.PromiseRelease) (bool, error) { promises := &v1alpha1.PromiseList{} err := o.client.List(o.ctx, promises, client.MatchingLabels{ promiseReleaseNameLabel: promiseRelease.GetName(), }) + if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to list promises: %w", err) + return false, fmt.Errorf("failed to list promises: %w", err) } switch len(promises.Items) { case 1: - if !promises.Items[0].GetDeletionTimestamp().IsZero() { - return defaultRequeue, nil - } - if promises.Items[0].Labels[promiseVersionLabel] == promiseRelease.Spec.Version { - break - } - fallthrough + return promises.Items[0].Labels[v1alpha1.PromiseVersionLabel] == promiseRelease.Spec.Version, nil case 0: - promiseRelease.Status.Installed = false - err = o.client.Status().Update(o.ctx, promiseRelease) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to update PromiseRelease status: %w", err) + return false, nil + default: + return false, fmt.Errorf("expected 0 or 1 promises, got %d", len(promises.Items)) + } +} + +func (r *PromiseReleaseReconciler) updateStatusAndConditions(o opts, pr *v1alpha1.PromiseRelease, + status string, conditionMessage, conditionReason string) { + pr.Status.Status = status + existingCondition := meta.FindStatusCondition(pr.Status.Conditions, "Installed") + if existingCondition != nil { + if existingCondition.Message == conditionMessage && existingCondition.Reason == conditionReason { + //don't update the status if its already correct + return } + } - return defaultRequeue, nil - default: - return ctrl.Result{}, fmt.Errorf("expected 0 or 1 promises, got %d", len(promises.Items)) + conditionStatus := v1.ConditionFalse + if conditionMessage == conditionMessageInstalled { + conditionStatus = v1.ConditionTrue } - return ctrl.Result{}, nil + condition := v1.Condition{ + Type: "Installed", + Message: conditionMessage, + Reason: conditionReason, + Status: conditionStatus, + } + + meta.SetStatusCondition(&pr.Status.Conditions, condition) + + err := o.client.Status().Update(o.ctx, pr) + if err != nil { + o.logger.Error(err, "Failed to update PromiseRelease status", "promiseReleaseName", pr.GetName(), "status", status, "condition", condition) + } } func (r *PromiseReleaseReconciler) validateVersion(o opts, promiseRelease *v1alpha1.PromiseRelease, promise *v1alpha1.Promise) (updated bool, err error) { - promiseVersion, found := promise.GetLabels()[promiseVersionLabel] + promiseVersion, found := promise.GetLabels()[v1alpha1.PromiseVersionLabel] if !found { - return false, fmt.Errorf("version label (%s) not found on promise; refusing to install", promiseVersionLabel) + r.updateStatusAndConditions(o, promiseRelease, statusErrorInstalling, "Version label not found on Promise", "VersionLabelNotFound") + return false, fmt.Errorf("version label (%s) not found on promise; refusing to install", v1alpha1.PromiseVersionLabel) } if promiseRelease.Spec.Version == "" { @@ -245,6 +277,8 @@ func (r *PromiseReleaseReconciler) validateVersion(o opts, promiseRelease *v1alp } if promiseVersion != promiseRelease.Spec.Version { + msg := fmt.Sprintf("Version labels do not match, found: %s, expected: %s", promiseVersion, promiseRelease.Spec.Version) + r.updateStatusAndConditions(o, promiseRelease, statusErrorInstalling, msg, "VersionNotMatching") return false, fmt.Errorf( "version label on promise (%s) does not match version on promise release (%s); refusing to install", promiseVersion, diff --git a/controllers/promiserelease_controller_test.go b/controllers/promiserelease_controller_test.go index 8cd67f17..276a342f 100644 --- a/controllers/promiserelease_controller_test.go +++ b/controllers/promiserelease_controller_test.go @@ -7,8 +7,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/syntasso/kratix/api/v1alpha1" + "github.com/syntasso/kratix/api/v1alpha1/v1alpha1fakes" "github.com/syntasso/kratix/controllers" - "github.com/syntasso/kratix/controllers/controllersfakes" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -21,12 +21,14 @@ var _ = Describe("PromiseReleaseController", func() { promiseRelease v1alpha1.PromiseRelease promiseReleaseNamespacedName types.NamespacedName reconciler *controllers.PromiseReleaseReconciler - fakeFetcher *controllersfakes.FakePromiseFetcher + fakeFetcher *v1alpha1fakes.FakePromiseFetcher promise *v1alpha1.Promise + err error + result ctrl.Result ) BeforeEach(func() { - fakeFetcher = &controllersfakes.FakePromiseFetcher{} + fakeFetcher = &v1alpha1fakes.FakePromiseFetcher{} fakeFetcher.FromURLReturns(promiseFromFile(promisePath), nil) reconciler = &controllers.PromiseReleaseReconciler{ @@ -61,13 +63,13 @@ var _ = Describe("PromiseReleaseController", func() { BeforeEach(func() { promise = promiseFromFile(promisePath) err := fakeK8sClient.Create(context.TODO(), promise) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) // Install the PromiseRelease err = fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred(), "reconciliation failed; expected it to work") + Expect(err).NotTo(HaveOccurred(), "reconciliation failed; expected it to work") promise = fetchPromise(promiseReleaseNamespacedName) }) @@ -92,21 +94,13 @@ var _ = Describe("PromiseReleaseController", func() { It("sets the promise release status to installed", func() { err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) - Expect(promiseRelease.Status.Installed).To(BeTrue()) - }) - }) - - When("the sourceRef type is unknown", func() { - It("errors", func() { - promiseRelease.Spec.SourceRef.Type = "unknown" - - err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) - - _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - - Expect(err).To(MatchError("unknown sourceRef type: unknown")) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Installed")) + Expect(promiseRelease.Status.Conditions).To(HaveLen(1)) + Expect(promiseRelease.Status.Conditions[0].Type).To(Equal("Installed")) + Expect(promiseRelease.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue)) + Expect(promiseRelease.Status.Conditions[0].Message).To(Equal("Installed successfully")) + Expect(promiseRelease.Status.Conditions[0].Reason).To(Equal("InstalledSuccessfully")) }) }) @@ -114,31 +108,54 @@ var _ = Describe("PromiseReleaseController", func() { When("the url fetcher errors", func() { BeforeEach(func() { fakeFetcher.FromURLReturns(nil, fmt.Errorf("can't do mate")) - err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + err = fakeK8sClient.Create(context.TODO(), &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) }) - It("installs the promise from the URL", func() { - _, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) + It("errors", func() { Expect(err).To(MatchError("failed to fetch promise from url: can't do mate")) }) + + It("updates the status", func() { + err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Error installing")) + Expect(promiseRelease.Status.Conditions).To(HaveLen(1)) + Expect(promiseRelease.Status.Conditions[0].Type).To(Equal("Installed")) + Expect(promiseRelease.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse)) + Expect(promiseRelease.Status.Conditions[0].Message).To(Equal("Failed to fetch Promise from URL")) + Expect(promiseRelease.Status.Conditions[0].Reason).To(Equal("FailedToFetchPromise")) + }) }) When("the url fetcher succeeds", func() { When("the Promise has no defined version", func() { + BeforeEach(func() { unversionedPromise := promiseFromFile(promisePath) unversionedPromise.Labels = nil fakeFetcher.FromURLReturns(unversionedPromise, nil) - err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + err = fakeK8sClient.Create(context.TODO(), &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + result, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) }) It("errors", func() { - result, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) Expect(result).To(Equal(ctrl.Result{})) Expect(err).To(MatchError(ContainSubstring("version label (kratix.io/promise-version) not found on promise; refusing to install"))) }) + + It("updates the status", func() { + err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Error installing")) + Expect(promiseRelease.Status.Conditions).To(HaveLen(1)) + Expect(promiseRelease.Status.Conditions[0].Type).To(Equal("Installed")) + Expect(promiseRelease.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse)) + Expect(promiseRelease.Status.Conditions[0].Message).To(Equal("Version label not found on Promise")) + Expect(promiseRelease.Status.Conditions[0].Reason).To(Equal("VersionLabelNotFound")) + }) }) When("the Promise version doesn't match the Promise Release version", func() { @@ -146,25 +163,36 @@ var _ = Describe("PromiseReleaseController", func() { unversionedPromise := promiseFromFile(promisePath) unversionedPromise.Labels["kratix.io/promise-version"] = "v2.2.0" fakeFetcher.FromURLReturns(unversionedPromise, nil) - err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + err = fakeK8sClient.Create(context.TODO(), &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + result, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) }) It("errors", func() { - result, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) Expect(result).To(Equal(ctrl.Result{})) Expect(err).To(MatchError(ContainSubstring("version label on promise (v2.2.0) does not match version on promise release (v1.1.0); refusing to install"))) }) + + It("updates the status", func() { + err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Error installing")) + Expect(promiseRelease.Status.Conditions).To(HaveLen(1)) + Expect(promiseRelease.Status.Conditions[0].Type).To(Equal("Installed")) + Expect(promiseRelease.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse)) + Expect(promiseRelease.Status.Conditions[0].Message).To(Equal("Version labels do not match, found: v2.2.0, expected: v1.1.0")) + Expect(promiseRelease.Status.Conditions[0].Reason).To(Equal("VersionNotMatching")) + }) }) When("the PromiseRelease does not specify a version", func() { BeforeEach(func() { promiseRelease.Spec.Version = "" - err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + err = fakeK8sClient.Create(context.TODO(), &promiseRelease) + Expect(err).NotTo(HaveOccurred()) _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) }) It("installs the promise from the URL", func() { @@ -174,22 +202,28 @@ var _ = Describe("PromiseReleaseController", func() { It("updates the PromiseRelease with the Promise version", func() { err := fakeK8sClient.Get(context.Background(), promiseReleaseNamespacedName, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) Expect(promiseRelease.Spec.Version).To(Equal("v1.1.0")) }) + + It("sets the promise release status to installed", func() { + err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Installed")) + }) }) When("the Promise has a defined version", func() { BeforeEach(func() { err := fakeK8sClient.Create(context.TODO(), &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) promise = fetchPromise(promiseReleaseNamespacedName) err = fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) }) It("installs the promise from the URL", func() { @@ -224,8 +258,10 @@ var _ = Describe("PromiseReleaseController", func() { })) }) - It("updates the promise release status to installed", func() { - Expect(promiseRelease.Status.Installed).To(BeTrue()) + It("sets the promise release status to installed", func() { + err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Installed")) }) It("adds a finalizer for the promise to the promise release", func() { @@ -237,15 +273,13 @@ var _ = Describe("PromiseReleaseController", func() { }) When("the PromiseRelease is deleted", func() { - var ( - err error - ) + var err error BeforeEach(func() { Expect(fakeK8sClient.Create(context.TODO(), &promiseRelease)).To(Succeed()) _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) promise := fetchPromise(promiseReleaseNamespacedName) promise.SetFinalizers([]string{"test-finalizer"}) @@ -258,12 +292,12 @@ var _ = Describe("PromiseReleaseController", func() { It("triggers the removal of the promise", func() { promise := fetchPromise(promiseReleaseNamespacedName) - Expect(promise.DeletionTimestamp).ToNot(BeNil()) + Expect(promise.DeletionTimestamp).NotTo(BeNil()) }) It("keeps the promise release finalizer", func() { err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) Expect(promiseRelease.GetFinalizers()).To(ConsistOf("kratix.io/promise-cleanup")) }) @@ -277,7 +311,7 @@ var _ = Describe("PromiseReleaseController", func() { deletePromise(promiseReleaseNamespacedName) _, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) err = fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) Expect(errors.IsNotFound(err)).To(BeTrue()) @@ -290,20 +324,20 @@ var _ = Describe("PromiseReleaseController", func() { Expect(fakeK8sClient.Create(context.Background(), &promiseRelease)).To(Succeed()) _, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) promise = fetchPromise(promiseReleaseNamespacedName) - Expect(promise).ToNot(BeNil()) + Expect(promise).NotTo(BeNil()) deletePromise(promiseReleaseNamespacedName) }) It("reinstalls the promise in the next reconciliation", func() { _, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) promise = fetchPromise(promiseReleaseNamespacedName) - Expect(promise).ToNot(BeNil()) + Expect(promise).NotTo(BeNil()) Expect(promise.DeletionTimestamp).To(BeNil()) }) }) @@ -313,7 +347,7 @@ var _ = Describe("PromiseReleaseController", func() { Expect(fakeK8sClient.Create(context.TODO(), &promiseRelease)).To(Succeed()) _, err := t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) fakeFetcher.FromURLReturns(promiseFromFile(updatedPromisePath), nil) Expect(fakeK8sClient.Get(context.Background(), promiseReleaseNamespacedName, &promiseRelease)).To(Succeed()) @@ -322,7 +356,7 @@ var _ = Describe("PromiseReleaseController", func() { Expect(fakeK8sClient.Update(context.Background(), &promiseRelease)).To(Succeed()) _, err = t.reconcileUntilCompletion(reconciler, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) promise = fetchPromise(promiseReleaseNamespacedName) }) @@ -354,8 +388,8 @@ var _ = Describe("PromiseReleaseController", func() { It("updates the promise release status", func() { err := fakeK8sClient.Get(context.TODO(), promiseReleaseNamespacedName, &promiseRelease) - Expect(err).ToNot(HaveOccurred()) - Expect(promiseRelease.Status.Installed).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + Expect(promiseRelease.Status.Status).To(Equal("Installed")) }) It("retains the promise release finalizer", func() { @@ -368,6 +402,4 @@ var _ = Describe("PromiseReleaseController", func() { Expect(promise.Spec).To(Equal(expectedPromise.Spec)) }) }) - - When("The PromiseRelease version is not specified", func() {}) }) diff --git a/controllers/scheduler.go b/controllers/scheduler.go index f97b45fe..95da6fa0 100644 --- a/controllers/scheduler.go +++ b/controllers/scheduler.go @@ -20,9 +20,9 @@ import ( ) const ( - workLabelKey = kratixPrefix + "work" - workloadGroupIDKey = kratixPrefix + "workload-group-id" - misscheduledLabel = kratixPrefix + "misscheduled" + workLabelKey = v1alpha1.KratixPrefix + "work" + workloadGroupIDKey = v1alpha1.KratixPrefix + "workload-group-id" + misscheduledLabel = v1alpha1.KratixPrefix + "misscheduled" ) type schedulingStatus string diff --git a/controllers/util.go b/controllers/util.go index b85a68b8..d404ee8a 100644 --- a/controllers/util.go +++ b/controllers/util.go @@ -23,11 +23,10 @@ import ( ) const ( - kratixPrefix = "kratix.io/" - promiseVersionLabel = kratixPrefix + "promise-version" - promiseReleaseNameLabel = kratixPrefix + "promise-release-name" - removeAllWorkflowJobsFinalizer = kratixPrefix + "workflows-cleanup" - runDeleteWorkflowsFinalizer = kratixPrefix + "delete-workflows" + promiseVersionLabel = v1alpha1.KratixPrefix + "promise-version" + promiseReleaseNameLabel = v1alpha1.KratixPrefix + "promise-release-name" + removeAllWorkflowJobsFinalizer = v1alpha1.KratixPrefix + "workflows-cleanup" + runDeleteWorkflowsFinalizer = v1alpha1.KratixPrefix + "delete-workflows" ) type StateStore interface { diff --git a/go.mod b/go.mod index 925495ba..de209472 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/go-logr/logr v1.3.0 github.com/gorilla/mux v1.8.1 github.com/minio/minio-go/v7 v7.0.23 - github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.13.2 github.com/onsi/gomega v1.30.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index ad45d6e6..bb13e2db 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,6 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= @@ -64,7 +62,6 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= @@ -75,20 +72,11 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -101,7 +89,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -147,17 +134,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -193,7 +171,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -234,13 +211,11 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 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= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -253,7 +228,6 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -262,18 +236,12 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -312,7 +280,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -327,12 +294,6 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -342,19 +303,14 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 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= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index e44cc86b..c23c5692 100644 --- a/main.go +++ b/main.go @@ -147,6 +147,10 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "PromiseRelease") os.Exit(1) } + if err = (&platformv1alpha1.PromiseRelease{}).SetupWebhookWithManager(mgr, &fetchers.URLFetcher{}); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "PromiseRelease") + os.Exit(1) + } //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {