diff --git a/Makefile b/Makefile index 212a9286d..62ce808dd 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,8 @@ clean: get-crds: ./hack/get_crds.sh https://github.com/crossplane-contrib/provider-helm provider-helm apis/release apis/helm ./hack/get_crds.sh https://github.com/crossplane-contrib/provider-kubernetes provider-kubernetes apis/object/v1alpha2 apis/kubernetes + # We don't need the conversion function and it messes with the v1alpha1 version + rm apis/kubernetes/v1alpha2/conversion.go # There is currently a bug with the serialization if `inline` and `omitempty` are set: https://github.com/crossplane/function-sdk-go/issues/161 $(sed) -i 's/inline,omitempty/inline/g' apis/helm/release/v1beta1/types.go # provider-sql needs manual fixes... Running this every time would break them. diff --git a/apis/kubernetes/v1alpha2/conversion.go b/apis/kubernetes/v1alpha1/doc.go similarity index 68% rename from apis/kubernetes/v1alpha2/conversion.go rename to apis/kubernetes/v1alpha1/doc.go index 0f284409b..66721d9fa 100644 --- a/apis/kubernetes/v1alpha2/conversion.go +++ b/apis/kubernetes/v1alpha1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2023 The Crossplane Authors. +Copyright 2020 The Crossplane Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,9 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha2 - -// Hub marks this type as a conversion hub. -func (g *Object) Hub() { - -} +// Package v1alpha1 contains the v1alpha1 group Object resources of the Kubernetes provider. +// +kubebuilder:object:generate=true +// +groupName=kubernetes.crossplane.io +// +versionName=v1alpha1 +package v1alpha1 diff --git a/apis/kubernetes/v1alpha1/management_policy_hack.go b/apis/kubernetes/v1alpha1/management_policy_hack.go new file mode 100644 index 000000000..f30c689ba --- /dev/null +++ b/apis/kubernetes/v1alpha1/management_policy_hack.go @@ -0,0 +1,81 @@ +package v1alpha1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// Note(turkenh): Provider Kubernetes Object already has a ManagementPolicy +// field and implements the logic in its own controller. +// This file contains temporary hacks until we remove the ManagementPolicy field +// from the Provider Kubernetes Object in favor of the one in the ResourceSpec. +// Ultimately, we should remove the ManagementPolicy field from the Provider +// Kubernetes Object and use the one in the ResourceSpec with the help of +// a conversion webhook. +// Something like https://github.com/crossplane/crossplane/pull/3822#issuecomment-1550039349 + +// A ResourceSpec defines the desired state of a managed resource. +type ResourceSpec struct { + // WriteConnectionSecretToReference specifies the namespace and name of a + // Secret to which any connection details for this managed resource should + // be written. Connection details frequently include the endpoint, username, + // and password required to connect to the managed resource. + // This field is planned to be replaced in a future release in favor of + // PublishConnectionDetailsTo. Currently, both could be set independently + // and connection details would be published to both without affecting + // each other. + // +optional + WriteConnectionSecretToReference *xpv1.SecretReference `json:"writeConnectionSecretToRef,omitempty"` + + // PublishConnectionDetailsTo specifies the connection secret config which + // contains a name, metadata and a reference to secret store config to + // which any connection details for this managed resource should be written. + // Connection details frequently include the endpoint, username, + // and password required to connect to the managed resource. + // +optional + PublishConnectionDetailsTo *xpv1.PublishConnectionDetailsTo `json:"publishConnectionDetailsTo,omitempty"` + + // ProviderConfigReference specifies how the provider that will be used to + // create, observe, update, and delete this managed resource should be + // configured. + // +kubebuilder:default={"name": "default"} + ProviderConfigReference *xpv1.Reference `json:"providerConfigRef,omitempty"` + + // ProviderReference specifies the provider that will be used to create, + // observe, update, and delete this managed resource. + // Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef` + ProviderReference *xpv1.Reference `json:"providerRef,omitempty"` + + // THIS IS AN ALPHA FIELD. Do not use it in production. It is not honored + // unless the relevant Crossplane feature flag is enabled, and may be + // changed or removed without notice. + // ManagementPolicy specifies the level of control Crossplane has over the + // managed external resource. + // This field is planned to replace the DeletionPolicy field in a future + // release. Currently, both could be set independently and non-default + // values would be honored if the feature flag is enabled. + // See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + // +optional + // +kubebuilder:default=FullControl + // ManagementPolicy xpv1.ManagementPolicy `json:"managementPolicy,omitempty"` + + // DeletionPolicy specifies what will happen to the underlying external + // when this managed resource is deleted - either "Delete" or "Orphan" the + // external resource. + // This field is planned to be deprecated in favor of the ManagementPolicy + // field in a future release. Currently, both could be set independently and + // non-default values would be honored if the feature flag is enabled. + // See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + // +optional + // +kubebuilder:default=Delete + DeletionPolicy xpv1.DeletionPolicy `json:"deletionPolicy,omitempty"` +} + +// GetManagementPolicies of this Object. +func (mg *Object) GetManagementPolicies() xpv1.ManagementPolicies { + // Note(turkenh): Crossplane runtime reconciler should leave handling of + // ManagementPolicies to the provider controller. This is a temporary hack + // until we remove the ManagementPolicy field from the Provider Kubernetes + // Object in favor of the one in the ResourceSpec. + return []xpv1.ManagementAction{xpv1.ManagementActionAll} +} + +// SetManagementPolicies of this Object. +func (mg *Object) SetManagementPolicies(r xpv1.ManagementPolicies) {} diff --git a/apis/kubernetes/v1alpha1/register.go b/apis/kubernetes/v1alpha1/register.go new file mode 100644 index 000000000..9be51be8c --- /dev/null +++ b/apis/kubernetes/v1alpha1/register.go @@ -0,0 +1,50 @@ +/* +Copyright 2020 The Crossplane Authors. + +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 ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "kubernetes.crossplane.io" + Version = "v1alpha1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) + +// Object type metadata. +var ( + ObjectKind = reflect.TypeOf(Object{}).Name() + ObjectGroupKind = schema.GroupKind{Group: Group, Kind: ObjectKind}.String() + ObjectKindAPIVersion = ObjectKind + "." + SchemeGroupVersion.String() + ObjectGroupVersionKind = SchemeGroupVersion.WithKind(ObjectKind) +) + +func init() { + SchemeBuilder.Register(&Object{}, &ObjectList{}) +} diff --git a/apis/kubernetes/v1alpha1/types.go b/apis/kubernetes/v1alpha1/types.go new file mode 100644 index 000000000..beaff8f04 --- /dev/null +++ b/apis/kubernetes/v1alpha1/types.go @@ -0,0 +1,254 @@ +/* +Copyright 2020 The Crossplane Authors. + +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 ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/fieldpath" +) + +// ObjectAction defines actions applicable to Object +type ObjectAction string + +// A ManagementPolicy determines what should happen to the underlying external +// resource when a managed resource is created, updated, deleted, or observed. +// +kubebuilder:validation:Enum=Default;ObserveCreateUpdate;ObserveDelete;Observe +type ManagementPolicy string + +const ( + // Default means the provider can fully manage the resource. + Default ManagementPolicy = "Default" + // ObserveCreateUpdate means the provider can observe, create, or update + // the resource, but can not delete it. + ObserveCreateUpdate ManagementPolicy = "ObserveCreateUpdate" + // ObserveDelete means the provider can observe or delete the resource, but + // can not create and update it. + ObserveDelete ManagementPolicy = "ObserveDelete" + // Observe means the provider can only observe the resource. + Observe ManagementPolicy = "Observe" + + // ObjectActionCreate means to create an Object + ObjectActionCreate ObjectAction = "Create" + // ObjectActionUpdate means to update an Object + ObjectActionUpdate ObjectAction = "Update" + // ObjectActionDelete means to delete an Object + ObjectActionDelete ObjectAction = "Delete" +) + +// DependsOn refers to an object by Name, Kind, APIVersion, etc. It is used to +// reference other Object or arbitrary Kubernetes resource which is either +// cluster or namespace scoped. +type DependsOn struct { + // APIVersion of the referenced object. + // +kubebuilder:default=kubernetes.crossplane.io/v1alpha1 + // +optional + APIVersion string `json:"apiVersion,omitempty"` + // Kind of the referenced object. + // +kubebuilder:default=Object + // +optional + Kind string `json:"kind,omitempty"` + // Name of the referenced object. + Name string `json:"name"` + // Namespace of the referenced object. + // +optional + Namespace string `json:"namespace,omitempty"` +} + +// PatchesFrom refers to an object by Name, Kind, APIVersion, etc., and patch +// fields from this object. +type PatchesFrom struct { + DependsOn `json:",inline"` + // FieldPath is the path of the field on the resource whose value is to be + // used as input. + FieldPath *string `json:"fieldPath"` +} + +// Reference refers to an Object or arbitrary Kubernetes resource and optionally +// patch values from that resource to the current Object. +type Reference struct { + // DependsOn is used to declare dependency on other Object or arbitrary + // Kubernetes resource. + // +optional + *DependsOn `json:"dependsOn,omitempty"` + // PatchesFrom is used to declare dependency on other Object or arbitrary + // Kubernetes resource, and also patch fields from this object. + // +optional + *PatchesFrom `json:"patchesFrom,omitempty"` + // ToFieldPath is the path of the field on the resource whose value will + // be changed with the result of transforms. Leave empty if you'd like to + // propagate to the same path as patchesFrom.fieldPath. + // +optional + ToFieldPath *string `json:"toFieldPath,omitempty"` +} + +// ObjectParameters are the configurable fields of a Object. +type ObjectParameters struct { + // Raw JSON representation of the kubernetes object to be created. + // +kubebuilder:validation:EmbeddedResource + // +kubebuilder:pruning:PreserveUnknownFields + Manifest runtime.RawExtension `json:"manifest"` +} + +// ObjectObservation are the observable fields of a Object. +type ObjectObservation struct { + // Raw JSON representation of the remote object. + // +kubebuilder:validation:EmbeddedResource + // +kubebuilder:pruning:PreserveUnknownFields + Manifest runtime.RawExtension `json:"manifest,omitempty"` +} + +// A ObjectSpec defines the desired state of a Object. +type ObjectSpec struct { + ResourceSpec `json:",inline"` + ConnectionDetails []ConnectionDetail `json:"connectionDetails,omitempty"` + ForProvider ObjectParameters `json:"forProvider"` + // +kubebuilder:default=Default + ManagementPolicy `json:"managementPolicy,omitempty"` + References []Reference `json:"references,omitempty"` + Readiness Readiness `json:"readiness,omitempty"` + // Watch enables watching the referenced or managed kubernetes resources. + // + // THIS IS AN ALPHA FIELD. Do not use it in production. It is not honored + // unless "watches" feature gate is enabled, and may be changed or removed + // without notice. + // +optional + // +kubebuilder:default=false + Watch bool `json:"watch,omitempty"` +} + +// ReadinessPolicy defines how the Object's readiness condition should be computed. +type ReadinessPolicy string + +const ( + // ReadinessPolicySuccessfulCreate means the object is marked as ready when the + // underlying external resource is successfully created. + ReadinessPolicySuccessfulCreate ReadinessPolicy = "SuccessfulCreate" + // ReadinessPolicyDeriveFromObject means the object is marked as ready if and only if the underlying + // external resource is considered ready. + ReadinessPolicyDeriveFromObject ReadinessPolicy = "DeriveFromObject" + + // ReadinessPolicyAllTrue means that all conditions have status true on the object. + // There must be at least one condition. + ReadinessPolicyAllTrue ReadinessPolicy = "AllTrue" +) + +// Readiness defines how the object's readiness condition should be computed, +// if not specified it will be considered ready as soon as the underlying external +// resource is considered up-to-date. +type Readiness struct { + // Policy defines how the Object's readiness condition should be computed. + // +optional + // +kubebuilder:validation:Enum=SuccessfulCreate;DeriveFromObject;AllTrue + // +kubebuilder:default=SuccessfulCreate + Policy ReadinessPolicy `json:"policy,omitempty"` +} + +// ConnectionDetail represents an entry in the connection secret for an Object +type ConnectionDetail struct { + v1.ObjectReference `json:",inline"` + ToConnectionSecretKey string `json:"toConnectionSecretKey,omitempty"` +} + +// A ObjectStatus represents the observed state of a Object. +type ObjectStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider ObjectObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// A Object is an provider Kubernetes API type +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="KIND",type="string",JSONPath=".spec.forProvider.manifest.kind" +// +kubebuilder:printcolumn:name="APIVERSION",type="string",JSONPath=".spec.forProvider.manifest.apiVersion",priority=1 +// +kubebuilder:printcolumn:name="METANAME",type="string",JSONPath=".spec.forProvider.manifest.metadata.name",priority=1 +// +kubebuilder:printcolumn:name="METANAMESPACE",type="string",JSONPath=".spec.forProvider.manifest.metadata.namespace",priority=1 +// +kubebuilder:printcolumn:name="PROVIDERCONFIG",type="string",JSONPath=".spec.providerConfigRef.name" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,kubernetes} +// +kubebuilder:deprecatedversion +// Deprecated: v1alpha1.Object is deprecated in favor of v1alpha2.Object +type Object struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ObjectSpec `json:"spec"` + Status ObjectStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ObjectList contains a list of Object +type ObjectList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Object `json:"items"` +} + +// ApplyFromFieldPathPatch patches the "to" resource, using a source field +// on the "from" resource. +func (r *Reference) ApplyFromFieldPathPatch(from, to runtime.Object) error { + // Default to patch the same field on the "to" resource. + if r.ToFieldPath == nil { + r.ToFieldPath = r.PatchesFrom.FieldPath + } + + paved, err := fieldpath.PaveObject(from) + if err != nil { + return err + } + + out, err := paved.GetValue(*r.PatchesFrom.FieldPath) + if err != nil { + return err + } + + return patchFieldValueToObject(*r.ToFieldPath, out, to) +} + +// patchFieldValueToObject, given a path, value and "to" object, will +// apply the value to the "to" object at the given path, returning +// any errors as they occur. +func patchFieldValueToObject(path string, value interface{}, to runtime.Object) error { + paved, err := fieldpath.PaveObject(to) + if err != nil { + return err + } + + err = paved.SetValue("spec.forProvider.manifest."+path, value) + if err != nil { + return err + } + + return runtime.DefaultUnstructuredConverter.FromUnstructured(paved.UnstructuredContent(), to) +} + +// IsActionAllowed determines if action is allowed to be performed on Object +func (p *ManagementPolicy) IsActionAllowed(action ObjectAction) bool { + if action == ObjectActionCreate || action == ObjectActionUpdate { + return *p == Default || *p == ObserveCreateUpdate + } + + // ObjectActionDelete + return *p == Default || *p == ObserveDelete +} diff --git a/apis/kubernetes/v1alpha1/zz_generated.deepcopy.go b/apis/kubernetes/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..c053e579f --- /dev/null +++ b/apis/kubernetes/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,280 @@ +//go:build !ignore_autogenerated + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "github.com/crossplane/crossplane-runtime/apis/common/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConnectionDetail) DeepCopyInto(out *ConnectionDetail) { + *out = *in + out.ObjectReference = in.ObjectReference +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConnectionDetail. +func (in *ConnectionDetail) DeepCopy() *ConnectionDetail { + if in == nil { + return nil + } + out := new(ConnectionDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DependsOn) DeepCopyInto(out *DependsOn) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependsOn. +func (in *DependsOn) DeepCopy() *DependsOn { + if in == nil { + return nil + } + out := new(DependsOn) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Object) DeepCopyInto(out *Object) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Object. +func (in *Object) DeepCopy() *Object { + if in == nil { + return nil + } + out := new(Object) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Object) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectList) DeepCopyInto(out *ObjectList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Object, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectList. +func (in *ObjectList) DeepCopy() *ObjectList { + if in == nil { + return nil + } + out := new(ObjectList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectObservation) DeepCopyInto(out *ObjectObservation) { + *out = *in + in.Manifest.DeepCopyInto(&out.Manifest) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectObservation. +func (in *ObjectObservation) DeepCopy() *ObjectObservation { + if in == nil { + return nil + } + out := new(ObjectObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectParameters) DeepCopyInto(out *ObjectParameters) { + *out = *in + in.Manifest.DeepCopyInto(&out.Manifest) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectParameters. +func (in *ObjectParameters) DeepCopy() *ObjectParameters { + if in == nil { + return nil + } + out := new(ObjectParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectSpec) DeepCopyInto(out *ObjectSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + if in.ConnectionDetails != nil { + in, out := &in.ConnectionDetails, &out.ConnectionDetails + *out = make([]ConnectionDetail, len(*in)) + copy(*out, *in) + } + in.ForProvider.DeepCopyInto(&out.ForProvider) + if in.References != nil { + in, out := &in.References, &out.References + *out = make([]Reference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Readiness = in.Readiness +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectSpec. +func (in *ObjectSpec) DeepCopy() *ObjectSpec { + if in == nil { + return nil + } + out := new(ObjectSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectStatus) DeepCopyInto(out *ObjectStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStatus. +func (in *ObjectStatus) DeepCopy() *ObjectStatus { + if in == nil { + return nil + } + out := new(ObjectStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PatchesFrom) DeepCopyInto(out *PatchesFrom) { + *out = *in + out.DependsOn = in.DependsOn + if in.FieldPath != nil { + in, out := &in.FieldPath, &out.FieldPath + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PatchesFrom. +func (in *PatchesFrom) DeepCopy() *PatchesFrom { + if in == nil { + return nil + } + out := new(PatchesFrom) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Readiness) DeepCopyInto(out *Readiness) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Readiness. +func (in *Readiness) DeepCopy() *Readiness { + if in == nil { + return nil + } + out := new(Readiness) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Reference) DeepCopyInto(out *Reference) { + *out = *in + if in.DependsOn != nil { + in, out := &in.DependsOn, &out.DependsOn + *out = new(DependsOn) + **out = **in + } + if in.PatchesFrom != nil { + in, out := &in.PatchesFrom, &out.PatchesFrom + *out = new(PatchesFrom) + (*in).DeepCopyInto(*out) + } + if in.ToFieldPath != nil { + in, out := &in.ToFieldPath, &out.ToFieldPath + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Reference. +func (in *Reference) DeepCopy() *Reference { + if in == nil { + return nil + } + out := new(Reference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) { + *out = *in + if in.WriteConnectionSecretToReference != nil { + in, out := &in.WriteConnectionSecretToReference, &out.WriteConnectionSecretToReference + *out = new(v1.SecretReference) + **out = **in + } + if in.PublishConnectionDetailsTo != nil { + in, out := &in.PublishConnectionDetailsTo, &out.PublishConnectionDetailsTo + *out = new(v1.PublishConnectionDetailsTo) + (*in).DeepCopyInto(*out) + } + if in.ProviderConfigReference != nil { + in, out := &in.ProviderConfigReference, &out.ProviderConfigReference + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.ProviderReference != nil { + in, out := &in.ProviderReference, &out.ProviderReference + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSpec. +func (in *ResourceSpec) DeepCopy() *ResourceSpec { + if in == nil { + return nil + } + out := new(ResourceSpec) + in.DeepCopyInto(out) + return out +} diff --git a/apis/syntools/v1alpha1/compositermariadbdatabaseinstance.go b/apis/syntools/v1alpha1/compositermariadbdatabaseinstance.go new file mode 100644 index 000000000..6d222d567 --- /dev/null +++ b/apis/syntools/v1alpha1/compositermariadbdatabaseinstance.go @@ -0,0 +1,35 @@ +package v1alpha1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true + +type CompositeMariaDBDatabaseInstance struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CompositeMariaDBUserInstanceSpec `json:"spec"` + Status CompositeMariaDBInstanceStatus `json:"status,omitempty"` +} + +type CompositeMariaDBDatabaseInstanceStatus struct { + xpv1.ResourceStatus `json:",inline"` +} + +type CompositeMariaDBDatabaseInstanceSpec struct { + xpv1.ResourceSpec `json:",inline"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true + +// CompositeMariaDBUserInstanceList represents a list of composites +type CompositeMariaDBDatabaseInstanceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []CompositeMariaDBDatabaseInstance `json:"items"` +} diff --git a/apis/syntools/v1alpha1/compositermariadb.go b/apis/syntools/v1alpha1/compositermariadbinstance.go similarity index 100% rename from apis/syntools/v1alpha1/compositermariadb.go rename to apis/syntools/v1alpha1/compositermariadbinstance.go diff --git a/apis/syntools/v1alpha1/compositermariadbuserinstance.go b/apis/syntools/v1alpha1/compositermariadbuserinstance.go new file mode 100644 index 000000000..5ecfc7a67 --- /dev/null +++ b/apis/syntools/v1alpha1/compositermariadbuserinstance.go @@ -0,0 +1,35 @@ +package v1alpha1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +kubebuilder:object:root=true + +type CompositeMariaDBUserInstance struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec CompositeMariaDBUserInstanceSpec `json:"spec"` + Status CompositeMariaDBInstanceStatus `json:"status,omitempty"` +} + +type CompositeMariaDBUserInstanceStatus struct { + xpv1.ResourceStatus `json:",inline"` +} + +type CompositeMariaDBUserInstanceSpec struct { + xpv1.ResourceSpec `json:",inline"` +} + +// +kubebuilder:object:generate=true +// +kubebuilder:object:root=true + +// CompositeMariaDBUserInstanceList represents a list of composites +type CompositeMariaDBUserInstanceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + + Items []CompositeMariaDBUserInstance `json:"items"` +} diff --git a/apis/syntools/v1alpha1/groupversion_info.go b/apis/syntools/v1alpha1/groupversion_info.go index 2c804d6d1..f12efd134 100644 --- a/apis/syntools/v1alpha1/groupversion_info.go +++ b/apis/syntools/v1alpha1/groupversion_info.go @@ -26,5 +26,9 @@ func init() { &CompositeRedisInstanceList{}, &CompositeMariaDBInstance{}, &CompositeMariaDBInstanceList{}, + &CompositeMariaDBDatabaseInstance{}, + &CompositeMariaDBDatabaseInstanceList{}, + &CompositeMariaDBUserInstance{}, + &CompositeMariaDBUserInstanceList{}, ) } diff --git a/apis/syntools/v1alpha1/zz_generated.deepcopy.go b/apis/syntools/v1alpha1/zz_generated.deepcopy.go index a1dae14e1..ac716dcc0 100644 --- a/apis/syntools/v1alpha1/zz_generated.deepcopy.go +++ b/apis/syntools/v1alpha1/zz_generated.deepcopy.go @@ -8,6 +8,97 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBDatabaseInstance) DeepCopyInto(out *CompositeMariaDBDatabaseInstance) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBDatabaseInstance. +func (in *CompositeMariaDBDatabaseInstance) DeepCopy() *CompositeMariaDBDatabaseInstance { + if in == nil { + return nil + } + out := new(CompositeMariaDBDatabaseInstance) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompositeMariaDBDatabaseInstance) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBDatabaseInstanceList) DeepCopyInto(out *CompositeMariaDBDatabaseInstanceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CompositeMariaDBDatabaseInstance, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBDatabaseInstanceList. +func (in *CompositeMariaDBDatabaseInstanceList) DeepCopy() *CompositeMariaDBDatabaseInstanceList { + if in == nil { + return nil + } + out := new(CompositeMariaDBDatabaseInstanceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompositeMariaDBDatabaseInstanceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBDatabaseInstanceSpec) DeepCopyInto(out *CompositeMariaDBDatabaseInstanceSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBDatabaseInstanceSpec. +func (in *CompositeMariaDBDatabaseInstanceSpec) DeepCopy() *CompositeMariaDBDatabaseInstanceSpec { + if in == nil { + return nil + } + out := new(CompositeMariaDBDatabaseInstanceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBDatabaseInstanceStatus) DeepCopyInto(out *CompositeMariaDBDatabaseInstanceStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBDatabaseInstanceStatus. +func (in *CompositeMariaDBDatabaseInstanceStatus) DeepCopy() *CompositeMariaDBDatabaseInstanceStatus { + if in == nil { + return nil + } + out := new(CompositeMariaDBDatabaseInstanceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CompositeMariaDBInstance) DeepCopyInto(out *CompositeMariaDBInstance) { *out = *in @@ -99,6 +190,97 @@ func (in *CompositeMariaDBInstanceStatus) DeepCopy() *CompositeMariaDBInstanceSt return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBUserInstance) DeepCopyInto(out *CompositeMariaDBUserInstance) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBUserInstance. +func (in *CompositeMariaDBUserInstance) DeepCopy() *CompositeMariaDBUserInstance { + if in == nil { + return nil + } + out := new(CompositeMariaDBUserInstance) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompositeMariaDBUserInstance) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBUserInstanceList) DeepCopyInto(out *CompositeMariaDBUserInstanceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CompositeMariaDBUserInstance, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBUserInstanceList. +func (in *CompositeMariaDBUserInstanceList) DeepCopy() *CompositeMariaDBUserInstanceList { + if in == nil { + return nil + } + out := new(CompositeMariaDBUserInstanceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompositeMariaDBUserInstanceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBUserInstanceSpec) DeepCopyInto(out *CompositeMariaDBUserInstanceSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBUserInstanceSpec. +func (in *CompositeMariaDBUserInstanceSpec) DeepCopy() *CompositeMariaDBUserInstanceSpec { + if in == nil { + return nil + } + out := new(CompositeMariaDBUserInstanceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompositeMariaDBUserInstanceStatus) DeepCopyInto(out *CompositeMariaDBUserInstanceStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompositeMariaDBUserInstanceStatus. +func (in *CompositeMariaDBUserInstanceStatus) DeepCopy() *CompositeMariaDBUserInstanceStatus { + if in == nil { + return nil + } + out := new(CompositeMariaDBUserInstanceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CompositeRedisInstance) DeepCopyInto(out *CompositeRedisInstance) { *out = *in diff --git a/apis/syntools/v1alpha1/zz_generated.managed.go b/apis/syntools/v1alpha1/zz_generated.managed.go index f2c23e93f..8c44e5b3d 100644 --- a/apis/syntools/v1alpha1/zz_generated.managed.go +++ b/apis/syntools/v1alpha1/zz_generated.managed.go @@ -4,6 +4,66 @@ package v1alpha1 import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +// GetCondition of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this CompositeMariaDBDatabaseInstance. +func (mg *CompositeMariaDBDatabaseInstance) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} + // GetCondition of this CompositeMariaDBInstance. func (mg *CompositeMariaDBInstance) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return mg.Status.GetCondition(ct) @@ -64,6 +124,66 @@ func (mg *CompositeMariaDBInstance) SetWriteConnectionSecretToReference(r *xpv1. mg.Spec.WriteConnectionSecretToReference = r } +// GetCondition of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this CompositeMariaDBUserInstance. +func (mg *CompositeMariaDBUserInstance) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} + // GetCondition of this CompositeRedisInstance. func (mg *CompositeRedisInstance) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return mg.Status.GetCondition(ct) diff --git a/apis/syntools/v1alpha1/zz_generated.managedlist.go b/apis/syntools/v1alpha1/zz_generated.managedlist.go index 0cf1c0438..827359e0c 100644 --- a/apis/syntools/v1alpha1/zz_generated.managedlist.go +++ b/apis/syntools/v1alpha1/zz_generated.managedlist.go @@ -4,6 +4,15 @@ package v1alpha1 import resource "github.com/crossplane/crossplane-runtime/pkg/resource" +// GetItems of this CompositeMariaDBDatabaseInstanceList. +func (l *CompositeMariaDBDatabaseInstanceList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} + // GetItems of this CompositeMariaDBInstanceList. func (l *CompositeMariaDBInstanceList) GetItems() []resource.Managed { items := make([]resource.Managed, len(l.Items)) @@ -13,6 +22,15 @@ func (l *CompositeMariaDBInstanceList) GetItems() []resource.Managed { return items } +// GetItems of this CompositeMariaDBUserInstanceList. +func (l *CompositeMariaDBUserInstanceList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} + // GetItems of this CompositeRedisInstanceList. func (l *CompositeRedisInstanceList) GetItems() []resource.Managed { items := make([]resource.Managed, len(l.Items)) diff --git a/cmd/controller.go b/cmd/controller.go index 90989309b..1d2333ec1 100644 --- a/cmd/controller.go +++ b/cmd/controller.go @@ -8,7 +8,6 @@ import ( "github.com/spf13/viper" "github.com/vshn/appcat/v4/pkg" "github.com/vshn/appcat/v4/pkg/controller/events" - "github.com/vshn/appcat/v4/pkg/controller/postgres" "github.com/vshn/appcat/v4/pkg/controller/webhooks" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -23,7 +22,9 @@ type controller struct { metricsAddr, healthAddr string leaderElect bool enableWebhooks bool + enableAppcatWebhooks bool enableQuotas bool + enableEventForwarding bool certDir string } @@ -44,8 +45,10 @@ func init() { ControllerCMD.Flags().BoolVar(&c.leaderElect, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") ControllerCMD.Flags().BoolVar(&c.enableWebhooks, "webhooks", true, "Disable the validation webhooks.") + ControllerCMD.Flags().BoolVar(&c.enableAppcatWebhooks, "appcat-webhooks", true, "Disable the appcat validation webhooks") ControllerCMD.Flags().StringVar(&c.certDir, "certdir", "/etc/webhook/certs", "Set the webhook certificate directory") ControllerCMD.Flags().BoolVar(&c.enableQuotas, "quotas", false, "Enable the quota webhooks, is only active if webhooks is also true") + ControllerCMD.Flags().BoolVar(&c.enableEventForwarding, "event-forwarding", true, "Disable event-forwarding") viper.AutomaticEnv() if !viper.IsSet("PLANS_NAMESPACE") { viper.Set("PLANS_NAMESPACE", "syn-appcat") @@ -74,25 +77,17 @@ func (c *controller) executeController(cmd *cobra.Command, _ []string) error { return err } - xpg := &postgres.XPostgreSQLReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - } - - err = xpg.SetupWithManager(mgr) - if err != nil { - return err - } - - events := &events.EventHandler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - } + if c.enableEventForwarding { + events := &events.EventHandler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + } - err = events.SetupWithManager(mgr) + err = events.SetupWithManager(mgr) - if err != nil { - return err + if err != nil { + return err + } } if c.enableWebhooks { @@ -101,7 +96,7 @@ func (c *controller) executeController(cmd *cobra.Command, _ []string) error { return fmt.Errorf("PLANS_NAMEPSACE env variable needs to be set for quota support") } - err := setupWebhooks(mgr, c.enableQuotas) + err := setupWebhooks(mgr, c.enableQuotas, c.enableAppcatWebhooks) if err != nil { return err } @@ -117,45 +112,70 @@ func (c *controller) executeController(cmd *cobra.Command, _ []string) error { return mgr.Start(ctrl.SetupSignalHandler()) } -func setupWebhooks(mgr manager.Manager, withQuota bool) error { - err := webhooks.SetupPostgreSQLWebhookHandlerWithManager(mgr, withQuota) - if err != nil { - return err - } - err = webhooks.SetupRedisWebhookHandlerWithManager(mgr, withQuota) - if err != nil { - return err +func setupWebhooks(mgr manager.Manager, withQuota bool, withAppcatWebhooks bool) error { + if withAppcatWebhooks { + err := webhooks.SetupPostgreSQLWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupRedisWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupMariaDBWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupMinioWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupNextcloudWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupKeycloakWebhookHandlerWithManager(mgr, withQuota) + if err != nil { + return err + } + err = webhooks.SetupXObjectbucketCDeletionProtectionHandlerWithManager(mgr) + if err != nil { + return err + } + + err = webhooks.SetupObjectbucketDeletionProtectionHandlerWithManager(mgr) + if err != nil { + return err + } } - err = webhooks.SetupMariaDBWebhookHandlerWithManager(mgr, withQuota) + + err := webhooks.SetupNamespaceDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - err = webhooks.SetupMinioWebhookHandlerWithManager(mgr, withQuota) + err = webhooks.SetupReleaseDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - err = webhooks.SetupNextcloudWebhookHandlerWithManager(mgr, withQuota) + err = webhooks.SetupMysqlDatabaseDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - err = webhooks.SetupKeycloakWebhookHandlerWithManager(mgr, withQuota) + err = webhooks.SetupMysqlGrantDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - err = webhooks.SetupNamespaceDeletionProtectionHandlerWithManager(mgr) + err = webhooks.SetupMysqlUserDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - - err = webhooks.SetupXObjectbucketCDeletionProtectionHandlerWithManager(mgr) + err = webhooks.SetupObjectDeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - - err = webhooks.SetupObjectbucketDeletionProtectionHandlerWithManager(mgr) + err = webhooks.SetupObjectv1alpha1DeletionProtectionHandlerWithManager(mgr) if err != nil { return err } - return webhooks.SetupPVCDeletionProtectionHandlerWithManager(mgr) } diff --git a/config/controller/cluster-role.yaml b/config/controller/cluster-role.yaml index 4cf5fa78e..41191e824 100644 --- a/config/controller/cluster-role.yaml +++ b/config/controller/cluster-role.yaml @@ -39,11 +39,65 @@ rules: - update - watch - apiGroups: - - kubernetes.crossplane.io + - syn.tools resources: - - objects + - compositemariadbdatabaseinstances verbs: - - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - syn.tools + resources: + - compositemariadbinstances + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - syn.tools + resources: + - compositemariadbinstances/status + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - syn.tools + resources: + - compositemariadbuserinstances + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - syn.tools + resources: + - compositeredisinstances + verbs: + - get + - list + - patch + - update + - watch +- apiGroups: + - syn.tools + resources: + - compositeredisinstances/status + verbs: + - get + - list + - patch + - update + - watch - apiGroups: - vshn.appcat.vshn.io resources: @@ -142,17 +196,6 @@ rules: - patch - update - watch -- apiGroups: - - vshn.appcat.vshn.io - resources: - - xvshnpostgresqls/finalizers - verbs: - - create - - get - - list - - patch - - update - - watch - apiGroups: - vshn.appcat.vshn.io resources: diff --git a/config/controller/webhooks.yaml b/config/controller/webhooks.yaml index 049497c9c..9575a59cb 100644 --- a/config/controller/webhooks.yaml +++ b/config/controller/webhooks.yaml @@ -4,6 +4,44 @@ kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-mysql-sql-crossplane-io-v1alpha1-database + failurePolicy: Fail + name: databases.mysql.vshn.appcat.vshn.io + rules: + - apiGroups: + - mysql.sql.crossplane.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - databases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-mysql-sql-crossplane-io-v1alpha1-grant + failurePolicy: Fail + name: grants.mysql.vshn.appcat.vshn.io + rules: + - apiGroups: + - mysql.sql.crossplane.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - grants + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -42,6 +80,44 @@ webhooks: resources: - objectbuckets sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-kubernetes-crossplane-io-v1alpha2-object + failurePolicy: Fail + name: objects.vshn.appcat.vshn.io + rules: + - apiGroups: + - kubernetes.crossplane.io + apiVersions: + - v1alpha2 + operations: + - DELETE + resources: + - objects + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-kubernetes-crossplane-io-v1alpha1-object + failurePolicy: Fail + name: objectsv1alpha1.vshn.appcat.vshn.io + rules: + - apiGroups: + - kubernetes.crossplane.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - objects + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -82,6 +158,44 @@ webhooks: resources: - persistentvolumeclaims sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-helm-crossplane-io-v1beta1-release + failurePolicy: Fail + name: releases.vshn.appcat.vshn.io + rules: + - apiGroups: + - helm.crossplane.io + apiVersions: + - v1beta1 + operations: + - DELETE + resources: + - releases + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-mysql-sql-crossplane-io-v1alpha1-user + failurePolicy: Fail + name: users.mysql.vshn.appcat.vshn.io + rules: + - apiGroups: + - mysql.sql.crossplane.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - users + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/crds/vshn.appcat.vshn.io_compositemariadbdatabaseinstances.yaml b/crds/vshn.appcat.vshn.io_compositemariadbdatabaseinstances.yaml new file mode 100644 index 000000000..13edfd9ed --- /dev/null +++ b/crds/vshn.appcat.vshn.io_compositemariadbdatabaseinstances.yaml @@ -0,0 +1,265 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: compositemariadbdatabaseinstances.vshn.appcat.vshn.io +spec: + group: vshn.appcat.vshn.io + names: + kind: CompositeMariaDBDatabaseInstance + listKind: CompositeMariaDBDatabaseInstanceList + plural: compositemariadbdatabaseinstances + singular: compositemariadbdatabaseinstance + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + deletionPolicy: + default: Delete + description: |- + DeletionPolicy specifies what will happen to the underlying external + when this managed resource is deleted - either "Delete" or "Orphan" the + external resource. + This field is planned to be deprecated in favor of the ManagementPolicies + field in a future release. Currently, both could be set independently and + non-default values would be honored if the feature flag is enabled. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + enum: + - Orphan + - Delete + type: string + managementPolicies: + default: + - '*' + description: |- + THIS IS A BETA FIELD. It is on by default but can be opted out + through a Crossplane feature flag. + ManagementPolicies specify the array of actions Crossplane is allowed to + take on the managed and external resources. + This field is planned to replace the DeletionPolicy field in a future + release. Currently, both could be set independently and non-default + values would be honored if the feature flag is enabled. If both are + custom, the DeletionPolicy field will be ignored. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md + items: + description: |- + A ManagementAction represents an action that the Crossplane controllers + can take on an external resource. + enum: + - Observe + - Create + - Update + - Delete + - LateInitialize + - '*' + type: string + type: array + providerConfigRef: + default: + name: default + description: |- + ProviderConfigReference specifies how the provider that will be used to + create, observe, update, and delete this managed resource should be + configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: |- + PublishConnectionDetailsTo specifies the connection secret config which + contains a name, metadata and a reference to secret store config to + which any connection details for this managed resource should be written. + Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: |- + SecretStoreConfigRef specifies which secret store config should be used + for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations are the annotations to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.annotations". + - It is up to Secret Store implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: |- + Labels are the labels/tags to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store types. + type: object + type: + description: |- + Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: |- + WriteConnectionSecretToReference specifies the namespace and name of a + Secret to which any connection details for this managed resource should + be written. Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + This field is planned to be replaced in a future release in favor of + PublishConnectionDetailsTo. Currently, both could be set independently + and connection details would be published to both without affecting + each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + type: object + status: + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time this condition transitioned from one + status to another. + format: date-time + type: string + message: + description: |- + A Message containing details about this condition's last transition from + one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: |- + Type of this condition. At most one of each condition type may apply to + a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/crds/vshn.appcat.vshn.io_compositemariadbuserinstances.yaml b/crds/vshn.appcat.vshn.io_compositemariadbuserinstances.yaml new file mode 100644 index 000000000..89a63a208 --- /dev/null +++ b/crds/vshn.appcat.vshn.io_compositemariadbuserinstances.yaml @@ -0,0 +1,265 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: compositemariadbuserinstances.vshn.appcat.vshn.io +spec: + group: vshn.appcat.vshn.io + names: + kind: CompositeMariaDBUserInstance + listKind: CompositeMariaDBUserInstanceList + plural: compositemariadbuserinstances + singular: compositemariadbuserinstance + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + properties: + deletionPolicy: + default: Delete + description: |- + DeletionPolicy specifies what will happen to the underlying external + when this managed resource is deleted - either "Delete" or "Orphan" the + external resource. + This field is planned to be deprecated in favor of the ManagementPolicies + field in a future release. Currently, both could be set independently and + non-default values would be honored if the feature flag is enabled. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + enum: + - Orphan + - Delete + type: string + managementPolicies: + default: + - '*' + description: |- + THIS IS A BETA FIELD. It is on by default but can be opted out + through a Crossplane feature flag. + ManagementPolicies specify the array of actions Crossplane is allowed to + take on the managed and external resources. + This field is planned to replace the DeletionPolicy field in a future + release. Currently, both could be set independently and non-default + values would be honored if the feature flag is enabled. If both are + custom, the DeletionPolicy field will be ignored. + See the design doc for more information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md + items: + description: |- + A ManagementAction represents an action that the Crossplane controllers + can take on an external resource. + enum: + - Observe + - Create + - Update + - Delete + - LateInitialize + - '*' + type: string + type: array + providerConfigRef: + default: + name: default + description: |- + ProviderConfigReference specifies how the provider that will be used to + create, observe, update, and delete this managed resource should be + configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: |- + PublishConnectionDetailsTo specifies the connection secret config which + contains a name, metadata and a reference to secret store config to + which any connection details for this managed resource should be written. + Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: |- + SecretStoreConfigRef specifies which secret store config should be used + for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: |- + Annotations are the annotations to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.annotations". + - It is up to Secret Store implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: |- + Labels are the labels/tags to be added to connection secret. + - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store types. + type: object + type: + description: |- + Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: |- + WriteConnectionSecretToReference specifies the namespace and name of a + Secret to which any connection details for this managed resource should + be written. Connection details frequently include the endpoint, username, + and password required to connect to the managed resource. + This field is planned to be replaced in a future release in favor of + PublishConnectionDetailsTo. Currently, both could be set independently + and connection details would be published to both without affecting + each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + type: object + status: + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time this condition transitioned from one + status to another. + format: date-time + type: string + message: + description: |- + A Message containing details about this condition's last transition from + one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: |- + Type of this condition. At most one of each condition type may apply to + a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true diff --git a/pkg/controller/postgres/deletion_protection.go b/pkg/controller/postgres/deletion_protection.go deleted file mode 100644 index 60f9025c0..000000000 --- a/pkg/controller/postgres/deletion_protection.go +++ /dev/null @@ -1,97 +0,0 @@ -package postgres - -import ( - "context" - "encoding/json" - "sort" - "strconv" - - logging "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/crossplane/crossplane-runtime/pkg/errors" - "github.com/go-logr/logr" - "github.com/vshn/appcat/v4/pkg/common/jsonpatch" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" -) - -const ( - finalizerName = "appcat.io/deletionProtection" -) - -func handle(ctx context.Context, inst client.Object) (client.Patch, error) { - log := logging.FromContext(ctx, "namespace", inst.GetNamespace(), "instance", inst.GetName()) - op := jsonpatch.JSONopNone - - removed := controllerutil.RemoveFinalizer(inst, finalizerName) - - if removed { - log.Info("Ensuring deprecated finalizer is not set", "objectName", inst.GetName()) - op = jsonpatch.JSONopRemove - } - - return getPatchObjectFinalizer(log, inst, op) -} - -func getPatchObjectFinalizer(log logr.Logger, inst client.Object, op jsonpatch.JSONop) (client.Patch, error) { - // handle the case if crossplane or something else decides to add more finalizers, or if - // the finalizer is already there. - index := len(inst.GetFinalizers()) - dupes := []int{} - for i, finalizer := range inst.GetFinalizers() { - if finalizer == finalizerName { - index = i - dupes = append(dupes, i) - } - } - - // if this is a noop and we have exactly one or no dupe, then we're done here - if op == jsonpatch.JSONopNone && (len(dupes) == 1 || len(dupes) == 0) { - return nil, nil - } - - log.V(1).Info("Index size", "size", index, "found finalizers", inst.GetFinalizers()) - - strIndex := strconv.Itoa(index) - if op == jsonpatch.JSONopAdd { - strIndex = "-" - } - - patchOps := []jsonpatch.JSONpatch{} - if op != jsonpatch.JSONopNone { - patchOps = append(patchOps, jsonpatch.JSONpatch{ - Op: op, - Path: "/metadata/finalizers/" + strIndex, - Value: finalizerName, - }) - } - - // if we have more than one of our finalizers, we need to remove the excess ones - if len(dupes) > 1 { - // Jsonpatch doesn't specify how deletion of multiple indices in an array is handled. - // When starting with the lowest first, it could be that the indices all shift and not match anymore. - // We reverse sort, so that the patch contains the largest index first. - // This way we avoid any index shifting during the deletion and are on the safe side. - sort.Sort(sort.Reverse(sort.IntSlice(dupes))) - for _, v := range dupes { - // We skip the first one, so we don't remove all of them - if v == 0 { - continue - } - patchOps = append(patchOps, jsonpatch.JSONpatch{ - Op: jsonpatch.JSONopRemove, - Path: "/metadata/finalizers/" + strconv.Itoa(v), - }) - } - } - - patch, err := json.Marshal(patchOps) - if err != nil { - return nil, errors.Wrap(err, "can't marshal patch") - } - - log.V(1).Info("Patching object", "patch", string(patch)) - - return client.RawPatch(types.JSONPatchType, patch), nil -} diff --git a/pkg/controller/postgres/deletion_protection_test.go b/pkg/controller/postgres/deletion_protection_test.go deleted file mode 100644 index ef34262fc..000000000 --- a/pkg/controller/postgres/deletion_protection_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package postgres - -import ( - "context" - "encoding/json" - "strconv" - "testing" - - "github.com/go-logr/logr" - "github.com/stretchr/testify/assert" - xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" - vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1" - "github.com/vshn/appcat/v4/pkg/common/jsonpatch" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func init() { - _ = vshnv1.AddToScheme(s) - _ = corev1.AddToScheme(s) - _ = xkube.SchemeBuilder.AddToScheme(s) -} - -func Test_Handle(t *testing.T) { - tests := map[string]struct { - ctx context.Context - obj vshnv1.XVSHNPostgreSQL - expectedPatch client.Patch - }{ - - "WhenFinalizer_ThenRemoveOpPatch": { - ctx: context.Background(), - obj: getXVSHNPostgreSQL(1), - expectedPatch: getPatch(jsonpatch.JSONopRemove), - }, - "WhenMultipleFinalzers_ThenOnlyKeepOne": { - ctx: context.Background(), - obj: getXVSHNPostgreSQL(2), - expectedPatch: getPatch(jsonpatch.JSONopRemove), - }, - "WhenMoreFinalzers_ThenOnlyKeepOne": { - ctx: context.Background(), - obj: getXVSHNPostgreSQL(3), - expectedPatch: getPatch(jsonpatch.JSONopRemove), - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - - // WHEN - actualPatch, err := handle(tc.ctx, &tc.obj) - - // THEN - assert.NoError(t, err) - assert.Equal(t, tc.expectedPatch, actualPatch) - }) - } -} - -func Test_GetPatchObjectFinalizer(t *testing.T) { - tests := map[string]struct { - obj vshnv1.XVSHNPostgreSQL - op jsonpatch.JSONop - expectedPatch client.Patch - }{ - "WhenOpRemove_ThenReturnPatch": { - obj: getXVSHNPostgreSQL(1), - op: jsonpatch.JSONopRemove, - expectedPatch: getPatch(jsonpatch.JSONopRemove), - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - - // GIVEN - log := logr.Discard() - - // WHEN - patch, err := getPatchObjectFinalizer(log, &tc.obj, tc.op) - - // THEN - assert.NoError(t, err) - assert.Equal(t, tc.expectedPatch, patch) - - }) - } -} - -func getXVSHNPostgreSQL(addFinalizer int) vshnv1.XVSHNPostgreSQL { - obj := vshnv1.XVSHNPostgreSQL{} - for i := 0; i < addFinalizer; i++ { - obj.Finalizers = append(obj.Finalizers, finalizerName) - } - return obj -} - -func getPatch(op jsonpatch.JSONop) client.Patch { - strIndex := strconv.Itoa(0) - if op == jsonpatch.JSONopAdd { - strIndex = "-" - } - patchOps := []jsonpatch.JSONpatch{ - { - Op: op, - Path: "/metadata/finalizers/" + strIndex, - Value: finalizerName, - }, - } - patch, _ := json.Marshal(patchOps) - return client.RawPatch(types.JSONPatchType, patch) -} diff --git a/pkg/controller/postgres/reconciler.go b/pkg/controller/postgres/reconciler.go deleted file mode 100644 index 2d0feb4b3..000000000 --- a/pkg/controller/postgres/reconciler.go +++ /dev/null @@ -1,109 +0,0 @@ -package postgres - -import ( - "context" - - "github.com/crossplane/crossplane-runtime/pkg/errors" - xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" - - vshnv1 "github.com/vshn/appcat/v4/apis/vshn/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/util/retry" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - logging "sigs.k8s.io/controller-runtime/pkg/log" -) - -//+kubebuilder:rbac:groups=kubernetes.crossplane.io,resources=objects,verbs=delete - -// To run on newer OpenShift version, this RBAC permission is necessary. -//+kubebuilder:rbac:groups=vshn.appcat.vshn.io,resources=xvshnpostgresqls/finalizers,verbs=get;list;patch;update;watch;create - -type XPostgreSQLReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (p *XPostgreSQLReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := logging.FromContext(ctx, "namespace", req.Namespace, "instance", req.Name) - inst := &vshnv1.XVSHNPostgreSQL{} - err := p.Get(ctx, req.NamespacedName, inst) - - if apierrors.IsNotFound(err) { - log.Info("Instance deleted") - return ctrl.Result{}, nil - } - - err = p.handleDeletionProtection(ctx, inst) - if err != nil { - return ctrl.Result{}, err - } - - return ctrl.Result{}, nil -} - -func (p *XPostgreSQLReconciler) handleDeletionProtection(ctx context.Context, inst *vshnv1.XVSHNPostgreSQL) error { - log := logging.FromContext(ctx, "namespace", inst.GetNamespace(), "instance", inst.GetName()) - - baseObj := &vshnv1.XVSHNPostgreSQL{ - ObjectMeta: metav1.ObjectMeta{ - Name: inst.Name, - Namespace: inst.Namespace, - }, - } - - patch, err := handle(ctx, inst) - - if err != nil { - return errors.Wrap(err, "cannot return patch operation object") - } - - if patch != nil { - - errorFunc := func(err error) bool { - return err != nil && !apierrors.IsNotFound(err) - } - - // Unfortunately patches just return generic errors if you patch something that has been modified. - // So we just retry a few times before actually logging and error. - err := retry.OnError(retry.DefaultBackoff, errorFunc, func() error { - log.V(1).Info("Trying to patch the object") - return p.Patch(ctx, baseObj, patch) - }) - - if err != nil { - return err - } - - } - - return nil -} - -func (p *XPostgreSQLReconciler) deletePostgresDB(ctx context.Context, inst *vshnv1.XVSHNPostgreSQL) error { - log := logging.FromContext(ctx, "namespace", inst.GetNamespace(), "instance", inst.GetName()) - - log.V(1).Info("Deleting sgcluster object") - o := &xkube.Object{ - ObjectMeta: metav1.ObjectMeta{ - Name: inst.Name + "-cluster", - }, - } - - err := p.Delete(ctx, o) - if err != nil && !apierrors.IsNotFound(err) { - return err - } - return nil -} - -// SetupWithManager sets up the controller with the Manager. -func (p *XPostgreSQLReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&vshnv1.XVSHNPostgreSQL{}). - Owns(&corev1.Namespace{}). - Complete(p) -} diff --git a/pkg/controller/postgres/reconciler_test.go b/pkg/controller/postgres/reconciler_test.go deleted file mode 100644 index 26d8ae9d4..000000000 --- a/pkg/controller/postgres/reconciler_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package postgres - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" - v1 "github.com/vshn/appcat/v4/apis/vshn/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/ptr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -var ( - s = runtime.NewScheme() -) - -func init() { - _ = v1.AddToScheme(s) - _ = corev1.AddToScheme(s) - _ = xkube.SchemeBuilder.AddToScheme(s) -} - -func Test_Reconcile(t *testing.T) { - tests := []struct { - name string - req reconcile.Request - inst v1.XVSHNPostgreSQL - instanceNamespace corev1.Namespace - expectedResult ctrl.Result - expectedError error - expectInstanceNamespace bool - }{ - { - name: "WhenFinalizer_ThenPatchInstance", - req: reconcile.Request{ - NamespacedName: types.NamespacedName{ - Name: "instance-1", - }, - }, - inst: v1.XVSHNPostgreSQL{ - ObjectMeta: metav1.ObjectMeta{ - Name: "instance-1", - Finalizers: []string{finalizerName}, - }, - Spec: v1.XVSHNPostgreSQLSpec{ - Parameters: v1.VSHNPostgreSQLParameters{ - Backup: v1.VSHNPostgreSQLBackup{ - DeletionProtection: ptr.To(true), - }, - }, - }, - }, - instanceNamespace: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "vshn-postgresql-instance-1", - }, - }, - expectedResult: ctrl.Result{}, - expectInstanceNamespace: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - - // GIVEN - fclient := fake.NewClientBuilder(). - WithScheme(s). - WithObjects(&tc.inst, &tc.instanceNamespace). - Build() - reconciler := XPostgreSQLReconciler{ - Client: fclient, - } - - // WHEN - result, err := reconciler.Reconcile(context.Background(), tc.req) - - // THEN - if tc.expectedError != nil { - assert.Error(t, tc.expectedError, err) - } else { - assert.NoError(t, err) - } - assert.Equal(t, tc.expectedResult, result) - - // Assert that the composite finalizers are as expected - resultComposite := &v1.XVSHNPostgreSQL{} - getObjectToAssert(t, resultComposite, fclient, client.ObjectKeyFromObject(&tc.inst)) - - // Assert that the namespace also has the finalizers - resultNs := &corev1.Namespace{} - if tc.expectInstanceNamespace { - getObjectToAssert(t, resultNs, fclient, client.ObjectKeyFromObject(&tc.instanceNamespace)) - } else { - assert.Error(t, fclient.Get(context.TODO(), client.ObjectKeyFromObject(&tc.instanceNamespace), resultNs)) - } - - assert.NotContains(t, resultComposite.GetFinalizers(), finalizerName) - }) - } -} - -func getObjectToAssert(t assert.TestingT, obj client.Object, fclient client.Client, key client.ObjectKey) { - err := fclient.Get(context.TODO(), key, obj) - assert.NoError(t, err) -} diff --git a/pkg/controller/webhooks/generic_deletionprotection_handler.go b/pkg/controller/webhooks/generic_deletionprotection_handler.go new file mode 100644 index 000000000..ef703cc50 --- /dev/null +++ b/pkg/controller/webhooks/generic_deletionprotection_handler.go @@ -0,0 +1,56 @@ +package webhooks + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +var _ webhook.CustomValidator = &GenericDeletionProtectionHandler{} + +type GenericDeletionProtectionHandler struct { + client client.Client + log logr.Logger +} + +// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type +func (p *GenericDeletionProtectionHandler) ValidateCreate(_ context.Context, _ runtime.Object) (admission.Warnings, error) { + // NOOP for now + return nil, nil +} + +// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type +func (p *GenericDeletionProtectionHandler) ValidateUpdate(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) { + // NOOP for now + return nil, nil +} + +// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type +func (p *GenericDeletionProtectionHandler) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + + resource, ok := obj.(client.Object) + if !ok { + return nil, fmt.Errorf("object is not valid") + } + + l := p.log.WithValues("object", resource.GetName(), "object", resource.GetNamespace(), "GVK", resource.GetObjectKind().GroupVersionKind().String()) + + compInfo, err := checkManagedObject(ctx, resource, p.client, l) + if err != nil { + return nil, err + } + + if compInfo.Exists { + l.Info("Blocking deletion of resource "+resource.GetName(), "parent", compInfo.Name) + return nil, fmt.Errorf(protectedMessage, "release", compInfo.Name) + } + + l.Info("Allowing deletion of resource "+resource.GetName(), "parent", compInfo.Name) + + return nil, nil +} diff --git a/pkg/controller/webhooks/mysql.go b/pkg/controller/webhooks/mysql.go new file mode 100644 index 000000000..98be2477e --- /dev/null +++ b/pkg/controller/webhooks/mysql.go @@ -0,0 +1,49 @@ +package webhooks + +import ( + mysqlv1alpha1 "github.com/vshn/appcat/v4/apis/sql/mysql/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" +) + +//+kubebuilder:webhook:verbs=delete,path=/validate-mysql-sql-crossplane-io-v1alpha1-database,mutating=false,failurePolicy=fail,groups="mysql.sql.crossplane.io",resources=databases,versions=v1alpha1,name=databases.mysql.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=delete,path=/validate-mysql-sql-crossplane-io-v1alpha1-grant,mutating=false,failurePolicy=fail,groups="mysql.sql.crossplane.io",resources=grants,versions=v1alpha1,name=grants.mysql.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=delete,path=/validate-mysql-sql-crossplane-io-v1alpha1-user,mutating=false,failurePolicy=fail,groups="mysql.sql.crossplane.io",resources=users,versions=v1alpha1,name=users.mysql.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 + +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbdatabaseinstances,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbuserinstances,verbs=get;list;watch;patch;update + +// SetupMysqlDatabaseDeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupMysqlDatabaseDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&mysqlv1alpha1.Database{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("mysql_database"), + }). + Complete() +} + +// SetupMysqlGrantDeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupMysqlGrantDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&mysqlv1alpha1.Grant{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("mysql_grant"), + }). + Complete() +} + +// SetupMysqlUserDeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupMysqlUserDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&mysqlv1alpha1.User{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("mysql_grant"), + }). + Complete() +} diff --git a/pkg/controller/webhooks/namespace.go b/pkg/controller/webhooks/namespace.go index 16e254a60..4b829d8ef 100644 --- a/pkg/controller/webhooks/namespace.go +++ b/pkg/controller/webhooks/namespace.go @@ -1,73 +1,20 @@ package webhooks import ( - "context" - "fmt" - - "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) //+kubebuilder:webhook:verbs=delete,path=/validate--v1-namespace,mutating=false,failurePolicy=fail,groups="",resources=namespaces,versions=v1,name=namespace.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 -var _ webhook.CustomValidator = &NamespaceDeletionProtectionHandler{} - -// NamespaceDeletionProtectionHandler -type NamespaceDeletionProtectionHandler struct { - client client.Client - log logr.Logger -} - // SetupNamespaceDeletionProtectionHandlerWithManager registers the validation webhook with the manager. func SetupNamespaceDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). For(&corev1.Namespace{}). - WithValidator(&NamespaceDeletionProtectionHandler{ + WithValidator(&GenericDeletionProtectionHandler{ client: mgr.GetClient(), log: mgr.GetLogger().WithName("webhook").WithName("namespace"), }). Complete() } - -// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type -func (p *NamespaceDeletionProtectionHandler) ValidateCreate(_ context.Context, _ runtime.Object) (admission.Warnings, error) { - // NOOP for now - return nil, nil -} - -// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type -func (p *NamespaceDeletionProtectionHandler) ValidateUpdate(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) { - // NOOP for now - return nil, nil -} - -// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type -func (p *NamespaceDeletionProtectionHandler) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { - - ns, ok := obj.(client.Object) - if !ok { - return nil, fmt.Errorf("object is not valid") - } - - l := p.log.WithValues("object", ns.GetName(), "namespace", ns.GetNamespace(), "GVK", ns.GetObjectKind().GroupVersionKind().String()) - - compInfo, err := checkManagedObject(ctx, ns, p.client, l) - if err != nil { - return nil, err - } - - if compInfo.Exists { - l.Info("Blocking deletion of namespace", "parent", compInfo.Name) - return nil, fmt.Errorf(protectedMessage, "namespace", compInfo.Name) - } - - l.Info("Allowing deletion of namespace", "parent", compInfo.Name) - - return nil, nil -} diff --git a/pkg/controller/webhooks/object.go b/pkg/controller/webhooks/object.go new file mode 100644 index 000000000..325788b59 --- /dev/null +++ b/pkg/controller/webhooks/object.go @@ -0,0 +1,39 @@ +package webhooks + +import ( + xkubev1alpha1 "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha1" + xkubev1alpha2 "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" + ctrl "sigs.k8s.io/controller-runtime" +) + +//+kubebuilder:webhook:verbs=delete,path=/validate-kubernetes-crossplane-io-v1alpha1-object,mutating=false,failurePolicy=fail,groups="kubernetes.crossplane.io",resources=objects,versions=v1alpha1,name=objectsv1alpha1.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 +//+kubebuilder:webhook:verbs=delete,path=/validate-kubernetes-crossplane-io-v1alpha2-object,mutating=false,failurePolicy=fail,groups="kubernetes.crossplane.io",resources=objects,versions=v1alpha2,name=objects.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 + +//+kubebuilder:rbac:groups=syn.tools,resources=compositeredisinstances,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositeredisinstances/status,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbinstances,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbinstances/status,verbs=get;list;watch;patch;update + +// SetupObjectDeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupObjectDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&xkubev1alpha2.Object{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("object"), + }). + Complete() +} + +// SetupObjectv1alpha1DeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupObjectv1alpha1DeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&xkubev1alpha1.Object{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("object"), + }). + Complete() +} diff --git a/pkg/controller/webhooks/release.go b/pkg/controller/webhooks/release.go new file mode 100644 index 000000000..db49ce50f --- /dev/null +++ b/pkg/controller/webhooks/release.go @@ -0,0 +1,25 @@ +package webhooks + +import ( + helmv1beta1 "github.com/vshn/appcat/v4/apis/helm/release/v1beta1" + ctrl "sigs.k8s.io/controller-runtime" +) + +//+kubebuilder:webhook:verbs=delete,path=/validate-helm-crossplane-io-v1beta1-release,mutating=false,failurePolicy=fail,groups="helm.crossplane.io",resources=releases,versions=v1beta1,name=releases.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1 + +//+kubebuilder:rbac:groups=syn.tools,resources=compositeredisinstances,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositeredisinstances/status,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbinstances,verbs=get;list;watch;patch;update +//+kubebuilder:rbac:groups=syn.tools,resources=compositemariadbinstances/status,verbs=get;list;watch;patch;update + +// SetupReleaseDeletionProtectionHandlerWithManager registers the validation webhook with the manager. +func SetupReleaseDeletionProtectionHandlerWithManager(mgr ctrl.Manager) error { + + return ctrl.NewWebhookManagedBy(mgr). + For(&helmv1beta1.Release{}). + WithValidator(&GenericDeletionProtectionHandler{ + client: mgr.GetClient(), + log: mgr.GetLogger().WithName("webhook").WithName("release"), + }). + Complete() +} diff --git a/pkg/scheme.go b/pkg/scheme.go index 7499ac06b..98896e2d4 100644 --- a/pkg/scheme.go +++ b/pkg/scheme.go @@ -10,6 +10,7 @@ import ( k8upv1 "github.com/k8up-io/k8up/v2/api/v1" promv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" alertmanagerv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" + xkubev1alpha1 "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha1" xkube "github.com/vshn/appcat/v4/apis/kubernetes/v1alpha2" my1alpha1 "github.com/vshn/appcat/v4/apis/sql/mysql/v1alpha1" pgv1alpha1 "github.com/vshn/appcat/v4/apis/sql/postgresql/v1alpha1" @@ -41,6 +42,7 @@ func SetupScheme() *runtime.Scheme { func AddToScheme(s *runtime.Scheme) { _ = corev1.SchemeBuilder.AddToScheme(s) _ = xkube.SchemeBuilder.AddToScheme(s) + _ = xkubev1alpha1.SchemeBuilder.AddToScheme(s) _ = vshnv1.SchemeBuilder.SchemeBuilder.AddToScheme(s) _ = stackgresv1.SchemeBuilder.AddToScheme(s) _ = stackgresv1beta1.SchemeBuilder.AddToScheme(s)