From 86ccaa50541c27f69a2430b3e0d12e0fedc57001 Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 10:58:23 +0300 Subject: [PATCH 01/26] Add Object v1alpha2 Signed-off-by: Hasan Turken --- apis/kubernetes.go | 4 +- apis/object/v1alpha2/doc.go | 21 + apis/object/v1alpha2/register.go | 50 +++ apis/object/v1alpha2/types.go | 244 ++++++++++ apis/object/v1alpha2/zz_generated.deepcopy.go | 260 +++++++++++ apis/object/v1alpha2/zz_generated.managed.go | 80 ++++ .../v1alpha2/zz_generated.managedlist.go | 29 ++ cmd/provider/main.go | 3 +- internal/controller/object/object.go | 60 +-- .../kubernetes.crossplane.io_objects.yaml | 418 ++++++++++++++++++ 10 files changed, 1136 insertions(+), 33 deletions(-) create mode 100644 apis/object/v1alpha2/doc.go create mode 100644 apis/object/v1alpha2/register.go create mode 100644 apis/object/v1alpha2/types.go create mode 100644 apis/object/v1alpha2/zz_generated.deepcopy.go create mode 100644 apis/object/v1alpha2/zz_generated.managed.go create mode 100644 apis/object/v1alpha2/zz_generated.managedlist.go diff --git a/apis/kubernetes.go b/apis/kubernetes.go index 6db35f6d..cf9a024e 100644 --- a/apis/kubernetes.go +++ b/apis/kubernetes.go @@ -20,7 +20,7 @@ package apis import ( "k8s.io/apimachinery/pkg/runtime" - objectv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" + objectv1alpha2 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -28,7 +28,7 @@ func init() { // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back AddToSchemes = append(AddToSchemes, templatev1alpha1.SchemeBuilder.AddToScheme, - objectv1alpha1.SchemeBuilder.AddToScheme, + objectv1alpha2.SchemeBuilder.AddToScheme, ) } diff --git a/apis/object/v1alpha2/doc.go b/apis/object/v1alpha2/doc.go new file mode 100644 index 00000000..8bee2199 --- /dev/null +++ b/apis/object/v1alpha2/doc.go @@ -0,0 +1,21 @@ +/* +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 v1alpha2 contains the v1alpha1 group Object resources of the Kubernetes provider. +// +kubebuilder:object:generate=true +// +groupName=kubernetes.crossplane.io +// +versionName=v1alpha2 +package v1alpha2 diff --git a/apis/object/v1alpha2/register.go b/apis/object/v1alpha2/register.go new file mode 100644 index 00000000..c2b92abc --- /dev/null +++ b/apis/object/v1alpha2/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 v1alpha2 + +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/object/v1alpha2/types.go b/apis/object/v1alpha2/types.go new file mode 100644 index 00000000..54cee2a4 --- /dev/null +++ b/apis/object/v1alpha2/types.go @@ -0,0 +1,244 @@ +/* +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 v1alpha2 + +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 { + xpv1.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"` +} + +// 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 + // +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:storageversion +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/object/v1alpha2/zz_generated.deepcopy.go b/apis/object/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 00000000..c0d78570 --- /dev/null +++ b/apis/object/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,260 @@ +//go:build !ignore_autogenerated + +/* +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. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "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 +} diff --git a/apis/object/v1alpha2/zz_generated.managed.go b/apis/object/v1alpha2/zz_generated.managed.go new file mode 100644 index 00000000..9ed35007 --- /dev/null +++ b/apis/object/v1alpha2/zz_generated.managed.go @@ -0,0 +1,80 @@ +/* +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. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha2 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetCondition of this Object. +func (mg *Object) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this Object. +func (mg *Object) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this Object. +func (mg *Object) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this Object. +func (mg *Object) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this Object. +func (mg *Object) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this Object. +func (mg *Object) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this Object. +func (mg *Object) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this Object. +func (mg *Object) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this Object. +func (mg *Object) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this Object. +func (mg *Object) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this Object. +func (mg *Object) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this Object. +func (mg *Object) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/object/v1alpha2/zz_generated.managedlist.go b/apis/object/v1alpha2/zz_generated.managedlist.go new file mode 100644 index 00000000..b5000727 --- /dev/null +++ b/apis/object/v1alpha2/zz_generated.managedlist.go @@ -0,0 +1,29 @@ +/* +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. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha2 + +import resource "github.com/crossplane/crossplane-runtime/pkg/resource" + +// GetItems of this ObjectList. +func (l *ObjectList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 2fdc6445..74035205 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -22,11 +22,12 @@ import ( "path/filepath" "time" + "sigs.k8s.io/controller-runtime/pkg/cache" + "go.uber.org/zap/zapcore" "gopkg.in/alecthomas/kingpin.v2" "k8s.io/client-go/tools/leaderelection/resourcelock" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/crossplane/crossplane-runtime/pkg/controller" diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index 436d7051..7b2a5019 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -43,7 +43,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" apisv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" "github.com/crossplane-contrib/provider-kubernetes/internal/clients" "github.com/crossplane-contrib/provider-kubernetes/internal/clients/azure" @@ -89,12 +89,12 @@ const ( // Setup adds a controller that reconciles Object managed resources. func Setup(mgr ctrl.Manager, o controller.Options) error { - name := managed.ControllerName(v1alpha1.ObjectGroupKind) + name := managed.ControllerName(v1alpha2.ObjectGroupKind) cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} r := managed.NewReconciler(mgr, - resource.ManagedKind(v1alpha1.ObjectGroupVersionKind), + resource.ManagedKind(v1alpha2.ObjectGroupVersionKind), managed.WithExternalConnecter(&connector{ logger: o.Logger, kube: mgr.GetClient(), @@ -116,7 +116,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { return ctrl.NewControllerManagedBy(mgr). Named(name). WithOptions(o.ForControllerRuntime()). - For(&v1alpha1.Object{}). + For(&v1alpha2.Object{}). Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) } @@ -138,7 +138,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E // This method is currently a little over our complexity goal - be wary // of making it more complex. - cr, ok := mg.(*v1alpha1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return nil, errors.New(errNotKubernetesObject) } @@ -233,7 +233,7 @@ type external struct { } func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { - cr, ok := mg.(*v1alpha1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalObservation{}, errors.New(errNotKubernetesObject) } @@ -276,14 +276,14 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex } func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { - cr, ok := mg.(*v1alpha1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalCreation{}, errors.New(errNotKubernetesObject) } c.logger.Debug("Creating", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionCreate) { + if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionCreate) { c.logger.Debug("External resource should not be created by provider, skip creating.") return managed.ExternalCreation{}, nil } @@ -305,14 +305,14 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { - cr, ok := mg.(*v1alpha1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalUpdate{}, errors.New(errNotKubernetesObject) } c.logger.Debug("Updating", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionUpdate) { + if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionUpdate) { c.logger.Debug("External resource should not be updated by provider, skip updating.") return managed.ExternalUpdate{}, nil } @@ -334,14 +334,14 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Delete(ctx context.Context, mg resource.Managed) error { - cr, ok := mg.(*v1alpha1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } c.logger.Debug("Deleting", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionDelete) { + if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionDelete) { c.logger.Debug("External resource should not be deleted by provider, skip deleting.") return nil } @@ -354,7 +354,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error { return errors.Wrap(resource.IgnoreNotFound(c.client.Delete(ctx, obj)), errDeleteObject) } -func getDesired(obj *v1alpha1.Object) (*unstructured.Unstructured, error) { +func getDesired(obj *v1alpha2.Object) (*unstructured.Unstructured, error) { desired := &unstructured.Unstructured{} if err := json.Unmarshal(obj.Spec.ForProvider.Manifest.Raw, desired); err != nil { return nil, errors.Wrap(err, errUnmarshalTemplate) @@ -366,7 +366,7 @@ func getDesired(obj *v1alpha1.Object) (*unstructured.Unstructured, error) { return desired, nil } -func getLastApplied(obj *v1alpha1.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { +func getLastApplied(obj *v1alpha2.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { lastApplied, ok := observed.GetAnnotations()[v1.LastAppliedConfigAnnotation] if !ok { return nil, nil @@ -384,7 +384,7 @@ func getLastApplied(obj *v1alpha1.Object, observed *unstructured.Unstructured) ( return last, nil } -func (c *external) setObserved(obj *v1alpha1.Object, observed *unstructured.Unstructured) error { +func (c *external) setObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { var err error if obj.Status.AtProvider.Manifest.Raw, err = observed.MarshalJSON(); err != nil { return errors.Wrap(err, errFailedToMarshalExisting) @@ -396,9 +396,9 @@ func (c *external) setObserved(obj *v1alpha1.Object, observed *unstructured.Unst return nil } -func (c *external) updateConditionFromObserved(obj *v1alpha1.Object, observed *unstructured.Unstructured) error { +func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { switch obj.Spec.Readiness.Policy { - case v1alpha1.ReadinessPolicyDeriveFromObject: + case v1alpha2.ReadinessPolicyDeriveFromObject: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -412,7 +412,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha1.Object, observed *u return nil } obj.SetConditions(xpv1.Available()) - case v1alpha1.ReadinessPolicyAllTrue: + case v1alpha2.ReadinessPolicyAllTrue: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -432,7 +432,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha1.Object, observed *u } else { obj.SetConditions(xpv1.Unavailable()) } - case v1alpha1.ReadinessPolicySuccessfulCreate, "": + case v1alpha2.ReadinessPolicySuccessfulCreate, "": // do nothing, will be handled by c.handleLastApplied method // "" should never happen, but just in case we will treat it as SuccessfulCreate for backward compatibility default: @@ -442,7 +442,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha1.Object, observed *u return nil } -func getReferenceInfo(ref v1alpha1.Reference) (string, string, string, string) { +func getReferenceInfo(ref v1alpha2.Reference) (string, string, string, string) { var apiVersion, kind, namespace, name string if ref.PatchesFrom != nil { @@ -465,7 +465,7 @@ func getReferenceInfo(ref v1alpha1.Reference) (string, string, string, string) { // resolveReferencies resolves references for the current Object. If it fails to // resolve some reference, e.g.: due to reference not ready, it will then return // error and requeue to wait for resolving it next time. -func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha1.Object) error { +func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) error { c.logger.Debug("Resolving referencies.") // Loop through references to resolve each referenced resource @@ -499,7 +499,7 @@ func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha1.Object) return nil } -func (c *external) isNotFound(obj *v1alpha1.Object, err error) bool { +func (c *external) isNotFound(obj *v1alpha2.Object, err error) bool { isNotFound := false if kerrors.IsNotFound(err) { @@ -510,7 +510,7 @@ func (c *external) isNotFound(obj *v1alpha1.Object, err error) bool { // external resource not found, so that Object can be deleted by managed // resource reconciler. Otherwise, the reconciler will try to delete the // external resource which breaks the management policy. - if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionDelete) { + if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionDelete) { c.logger.Debug("Managed resource was deleted but external resource is undeletable.") isNotFound = true } @@ -519,10 +519,10 @@ func (c *external) isNotFound(obj *v1alpha1.Object, err error) bool { return isNotFound } -func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha1.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { +func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { isUpToDate := false - if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha1.ObjectActionUpdate) { + if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionUpdate) { // Treated as up-to-date to skip last applied annotation update since we // do not create or update the external resource. isUpToDate = true @@ -534,7 +534,7 @@ func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha1.Object, if isUpToDate { c.logger.Debug("Up to date!") - if p := obj.Spec.Readiness.Policy; p == v1alpha1.ReadinessPolicySuccessfulCreate || p == "" { + if p := obj.Spec.Readiness.Policy; p == v1alpha2.ReadinessPolicySuccessfulCreate || p == "" { obj.Status.SetConditions(xpv1.Available()) } @@ -563,7 +563,7 @@ type objFinalizer struct { type refFinalizerFn func(context.Context, *unstructured.Unstructured, string) error -func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha1.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { +func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha2.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { // Loop through references to resolve each referenced resource for _, ref := range obj.Spec.References { if ref.DependsOn == nil && ref.PatchesFrom == nil { @@ -599,7 +599,7 @@ func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha1.Obj } func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1alpha1.Object) + obj, ok := res.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -629,7 +629,7 @@ func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) er } func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1alpha1.Object) + obj, ok := res.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -658,7 +658,7 @@ func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) return errors.Wrap(err, errRemoveFinalizer) } -func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1alpha1.ConnectionDetail) (managed.ConnectionDetails, error) { +func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1alpha2.ConnectionDetail) (managed.ConnectionDetails, error) { mcd := managed.ConnectionDetails{} for _, cd := range connDetails { diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index 3513d299..163e5d01 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -445,6 +445,424 @@ spec: - spec type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.forProvider.manifest.kind + name: KIND + type: string + - jsonPath: .spec.forProvider.manifest.apiVersion + name: APIVERSION + priority: 1 + type: string + - jsonPath: .spec.forProvider.manifest.metadata.name + name: METANAME + priority: 1 + type: string + - jsonPath: .spec.forProvider.manifest.metadata.namespace + name: METANAMESPACE + priority: 1 + type: string + - jsonPath: .spec.providerConfigRef.name + name: PROVIDERCONFIG + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: A Object is an provider Kubernetes API type + 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: + description: A ObjectSpec defines the desired state of a Object. + properties: + connectionDetails: + items: + description: ConnectionDetail represents an entry in the connection + secret for an Object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of + an entire object, this string should contain a valid JSON/Go + field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part of + an object. TODO: this design is not final and this field is + subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + toConnectionSecretKey: + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + type: array + 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 + forProvider: + description: ObjectParameters are the configurable fields of a Object. + properties: + manifest: + description: Raw JSON representation of the kubernetes object + to be created. + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + required: + - manifest + type: object + 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 + managementPolicy: + default: Default + description: A ManagementPolicy determines what should happen to the + underlying external resource when a managed resource is created, + updated, deleted, or observed. + enum: + - Default + - ObserveCreateUpdate + - ObserveDelete + - Observe + type: string + 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 + readiness: + description: 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. + properties: + policy: + default: SuccessfulCreate + description: Policy defines how the Object's readiness condition + should be computed. + enum: + - SuccessfulCreate + - DeriveFromObject + type: string + type: object + references: + items: + description: Reference refers to an Object or arbitrary Kubernetes + resource and optionally patch values from that resource to the + current Object. + properties: + dependsOn: + description: DependsOn is used to declare dependency on other + Object or arbitrary Kubernetes resource. + properties: + apiVersion: + default: kubernetes.crossplane.io/v1alpha1 + description: APIVersion of the referenced object. + type: string + kind: + default: Object + description: Kind of the referenced object. + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. + type: string + required: + - name + type: object + patchesFrom: + description: PatchesFrom is used to declare dependency on other + Object or arbitrary Kubernetes resource, and also patch fields + from this object. + properties: + apiVersion: + default: kubernetes.crossplane.io/v1alpha1 + description: APIVersion of the referenced object. + type: string + fieldPath: + description: FieldPath is the path of the field on the resource + whose value is to be used as input. + type: string + kind: + default: Object + description: Kind of the referenced object. + type: string + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object. + type: string + required: + - fieldPath + - name + type: object + toFieldPath: + description: 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. + type: string + type: object + type: array + 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 + required: + - forProvider + type: object + status: + description: A ObjectStatus represents the observed state of a Object. + properties: + atProvider: + description: ObjectObservation are the observable fields of a Object. + properties: + manifest: + description: Raw JSON representation of the remote object. + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + type: object + 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 subresources: status: {} From 1630dc81b17cd46d39eb8354bbf9dd8d1d55c665 Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 13:40:21 +0300 Subject: [PATCH 02/26] Remove managementPolicy from the controller Signed-off-by: Hasan Turken --- apis/object/v1alpha2/types.go | 16 ++--------- internal/controller/object/object.go | 43 ++-------------------------- 2 files changed, 4 insertions(+), 55 deletions(-) diff --git a/apis/object/v1alpha2/types.go b/apis/object/v1alpha2/types.go index 54cee2a4..7f5f2013 100644 --- a/apis/object/v1alpha2/types.go +++ b/apis/object/v1alpha2/types.go @@ -120,10 +120,8 @@ type ObjectSpec struct { xpv1.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"` + References []Reference `json:"references,omitempty"` + Readiness Readiness `json:"readiness,omitempty"` } // ReadinessPolicy defines how the Object's readiness condition should be computed. @@ -232,13 +230,3 @@ func patchFieldValueToObject(path string, value interface{}, to runtime.Object) 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/internal/controller/object/object.go b/internal/controller/object/object.go index 7b2a5019..c4423999 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -256,7 +256,7 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex Name: observed.GetName(), }, observed) - if c.isNotFound(cr, err) { + if kerrors.IsNotFound(err) { return managed.ExternalObservation{ResourceExists: false}, nil } @@ -283,11 +283,6 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext c.logger.Debug("Creating", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionCreate) { - c.logger.Debug("External resource should not be created by provider, skip creating.") - return managed.ExternalCreation{}, nil - } - obj, err := getDesired(cr) if err != nil { return managed.ExternalCreation{}, err @@ -312,11 +307,6 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext c.logger.Debug("Updating", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionUpdate) { - c.logger.Debug("External resource should not be updated by provider, skip updating.") - return managed.ExternalUpdate{}, nil - } - obj, err := getDesired(cr) if err != nil { return managed.ExternalUpdate{}, err @@ -341,11 +331,6 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error { c.logger.Debug("Deleting", "resource", cr) - if !cr.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionDelete) { - c.logger.Debug("External resource should not be deleted by provider, skip deleting.") - return nil - } - obj, err := getDesired(cr) if err != nil { return err @@ -499,34 +484,10 @@ func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) return nil } -func (c *external) isNotFound(obj *v1alpha2.Object, err error) bool { - isNotFound := false - - if kerrors.IsNotFound(err) { - isNotFound = true - } else if meta.WasDeleted(obj) { - // If the Object resource was being deleted but the external resource is - // not deletable as management policy is specified, we should return the - // external resource not found, so that Object can be deleted by managed - // resource reconciler. Otherwise, the reconciler will try to delete the - // external resource which breaks the management policy. - if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionDelete) { - c.logger.Debug("Managed resource was deleted but external resource is undeletable.") - isNotFound = true - } - } - - return isNotFound -} - func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { isUpToDate := false - if !obj.Spec.ManagementPolicy.IsActionAllowed(v1alpha2.ObjectActionUpdate) { - // Treated as up-to-date to skip last applied annotation update since we - // do not create or update the external resource. - isUpToDate = true - } else if last != nil && equality.Semantic.DeepEqual(last, desired) { + if last != nil && equality.Semantic.DeepEqual(last, desired) { // Mark as up-to-date since last is equal to desired isUpToDate = true } From 6acc4fb021adc4c00713e212f5abc2ecab173b95 Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 15:18:28 +0300 Subject: [PATCH 03/26] Add conversion between v1alpha1 and v1alpha2 Signed-off-by: Hasan Turken --- apis/object/v1alpha1/conversion.go | 57 ++++++++++++++++++++++++++++++ apis/object/v1alpha2/conversion.go | 5 +++ cmd/provider/main.go | 10 ++++-- 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 apis/object/v1alpha1/conversion.go create mode 100644 apis/object/v1alpha2/conversion.go diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go new file mode 100644 index 00000000..560640a7 --- /dev/null +++ b/apis/object/v1alpha1/conversion.go @@ -0,0 +1,57 @@ +package v1alpha1 + +import ( + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +// ConvertTo converts this Object to the Hub version (v1alpha2). +func (src *Object) ConvertTo(dstRaw conversion.Hub) error { + dst := dstRaw.(*v1alpha2.Object) + + switch src.Spec.ManagementPolicy { + case Default: + dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionAll} + case ObserveCreateUpdate: + dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionObserve, xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate} + case ObserveDelete: + dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionObserve, xpv1.ManagementActionDelete} + case Observe: + dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionObserve} + default: + return errors.New("unknown management policy") + } + + return nil +} + +// ConvertFrom converts from the Hub version (v1alpha2) to this version. +func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { + src := srcRaw.(*v1alpha2.Object) + + policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) + + switch { + case policySet.Has(xpv1.ManagementActionAll): + dst.Spec.ManagementPolicy = Default + case policySet.HasAll(xpv1.ManagementActionObserve, xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate, xpv1.ManagementActionDelete): + dst.Spec.ManagementPolicy = Default + case policySet.HasAll(xpv1.ManagementActionObserve, xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate) && + !policySet.Has(xpv1.ManagementActionDelete): + dst.Spec.ManagementPolicy = ObserveCreateUpdate + case policySet.HasAll(xpv1.ManagementActionObserve, xpv1.ManagementActionDelete) && + !policySet.HasAny(xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate): + dst.Spec.ManagementPolicy = ObserveDelete + case policySet.Has(xpv1.ManagementActionObserve) && + !policySet.HasAny(xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate, xpv1.ManagementActionDelete): + dst.Spec.ManagementPolicy = Observe + default: + // TODO(turkenh): Should we default to something here instead of erroring out? + return errors.New("unsupported management policy") + } + + return nil +} diff --git a/apis/object/v1alpha2/conversion.go b/apis/object/v1alpha2/conversion.go new file mode 100644 index 00000000..a61c11ae --- /dev/null +++ b/apis/object/v1alpha2/conversion.go @@ -0,0 +1,5 @@ +package v1alpha2 + +func (g *Object) Hub() { + +} diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 74035205..71f2e8de 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -22,13 +22,13 @@ import ( "path/filepath" "time" - "sigs.k8s.io/controller-runtime/pkg/cache" - "go.uber.org/zap/zapcore" "gopkg.in/alecthomas/kingpin.v2" "k8s.io/client-go/tools/leaderelection/resourcelock" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/crossplane/crossplane-runtime/pkg/controller" "github.com/crossplane/crossplane-runtime/pkg/feature" @@ -36,6 +36,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" "github.com/crossplane-contrib/provider-kubernetes/apis" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" object "github.com/crossplane-contrib/provider-kubernetes/internal/controller" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -83,6 +84,9 @@ func main() { LeaderElectionResourceLock: resourcelock.LeasesResourceLock, LeaseDuration: func() *time.Duration { d := 60 * time.Second; return &d }(), RenewDeadline: func() *time.Duration { d := 50 * time.Second; return &d }(), + WebhookServer: webhook.NewServer(webhook.Options{ + CertDir: filepath.Join("/", "webhook", "tls"), + }), }) kingpin.FatalIfError(err, "Cannot create controller manager") @@ -95,6 +99,8 @@ func main() { Features: &feature.Flags{}, } + kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1alpha2.Object{}).Complete(), "Cannot create Object webhook") + kingpin.FatalIfError(object.Setup(mgr, o), "Cannot setup controller") kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") } From 7ed493b6027ffe0c68f5422cb18ebaf6c0ea7f3b Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 15:30:46 +0300 Subject: [PATCH 04/26] Fix unit tests after offloading management policies to runtime Signed-off-by: Hasan Turken --- apis/object/v1alpha1/conversion.go | 10 +- apis/object/v1alpha2/conversion.go | 1 + internal/controller/object/object_test.go | 236 ++++++------------ .../kubernetes.crossplane.io_objects.yaml | 11 - 4 files changed, 83 insertions(+), 175 deletions(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 560640a7..5f5d18ff 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -1,15 +1,17 @@ package v1alpha1 import ( - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" - xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/conversion" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" ) // ConvertTo converts this Object to the Hub version (v1alpha2). -func (src *Object) ConvertTo(dstRaw conversion.Hub) error { +func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. dst := dstRaw.(*v1alpha2.Object) switch src.Spec.ManagementPolicy { @@ -29,7 +31,7 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { } // ConvertFrom converts from the Hub version (v1alpha2) to this version. -func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { +func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. src := srcRaw.(*v1alpha2.Object) policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) diff --git a/apis/object/v1alpha2/conversion.go b/apis/object/v1alpha2/conversion.go index a61c11ae..69917e4a 100644 --- a/apis/object/v1alpha2/conversion.go +++ b/apis/object/v1alpha2/conversion.go @@ -1,5 +1,6 @@ package v1alpha2 +// Hub marks this type as a conversion hub. func (g *Object) Hub() { } diff --git a/internal/controller/object/object_test.go b/internal/controller/object/object_test.go index 11db7152..0ba4c81c 100644 --- a/internal/controller/object/object_test.go +++ b/internal/controller/object/object_test.go @@ -42,7 +42,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" kubernetesv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -80,31 +80,30 @@ type notKubernetesObject struct { resource.Managed } -type kubernetesObjectModifier func(obj *v1alpha1.Object) +type kubernetesObjectModifier func(obj *v1alpha2.Object) type externalResourceModifier func(res *unstructured.Unstructured) -func kubernetesObject(om ...kubernetesObjectModifier) *v1alpha1.Object { - o := &v1alpha1.Object{ +func kubernetesObject(om ...kubernetesObjectModifier) *v1alpha2.Object { + o := &v1alpha2.Object{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: v1alpha1.ObjectKind, + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.ObjectKind, }, ObjectMeta: metav1.ObjectMeta{ Name: testObjectName, Namespace: testNamespace, }, - Spec: v1alpha1.ObjectSpec{ - ManagementPolicy: v1alpha1.Default, - ResourceSpec: v1alpha1.ResourceSpec{ + Spec: v1alpha2.ObjectSpec{ + ResourceSpec: xpv1.ResourceSpec{ ProviderConfigReference: &xpv1.Reference{ Name: providerName, }, }, - ForProvider: v1alpha1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: externalResourceRaw}, }, }, - Status: v1alpha1.ObjectStatus{}, + Status: v1alpha2.ObjectStatus{}, } for _, m := range om { @@ -146,16 +145,16 @@ func upToDateExternalResource() *unstructured.Unstructured { return externalResourceWithLastAppliedConfigAnnotation(string(externalResourceRaw)) } -func objectReferences() []v1alpha1.Reference { - dependsOn := v1alpha1.DependsOn{ - APIVersion: v1alpha1.SchemeGroupVersion.String(), - Kind: v1alpha1.ObjectKind, +func objectReferences() []v1alpha2.Reference { + dependsOn := v1alpha2.DependsOn{ + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.ObjectKind, Name: testReferenceObjectName, Namespace: testNamespace, } - ref := []v1alpha1.Reference{ + ref := []v1alpha2.Reference{ { - PatchesFrom: &v1alpha1.PatchesFrom{ + PatchesFrom: &v1alpha2.PatchesFrom{ DependsOn: dependsOn, }, }, @@ -169,8 +168,8 @@ func objectReferences() []v1alpha1.Reference { func referenceObject(rm ...externalResourceModifier) *unstructured.Unstructured { obj := &unstructured.Unstructured{ Object: map[string]interface{}{ - "apiVersion": v1alpha1.SchemeGroupVersion.String(), - "kind": v1alpha1.ObjectKind, + "apiVersion": v1alpha2.SchemeGroupVersion.String(), + "kind": v1alpha2.ObjectKind, "metadata": map[string]interface{}{ "name": testReferenceObjectName, "namespace": testNamespace, @@ -708,7 +707,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -768,7 +767,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "UpToDateNameDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -815,32 +814,9 @@ func Test_helmExternal_Observe(t *testing.T) { err: nil, }, }, - "UpToDateIfManagementPolicyDefined": { - args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.ManagementPolicy = "ObserveDelete" - }), - client: resource.ClientApplicator{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { - *obj.(*unstructured.Unstructured) = *externalResource() - return nil - }), - }, - }, - }, - want: want{ - out: managed.ExternalObservation{ - ResourceExists: true, - ResourceUpToDate: true, - ConnectionDetails: managed.ConnectionDetails{}, - }, - err: nil, - }, - }, "FailedToPatchFieldFromReferenceObject": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() obj.Spec.References[0].PatchesFrom.FieldPath = pointer.String("nonexistent_field") }), @@ -861,7 +837,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -878,7 +854,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoExternalResourceExistsIfObjectWasDeleted": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()} obj.Spec.References = objectReferences() }), @@ -902,37 +878,9 @@ func Test_helmExternal_Observe(t *testing.T) { err: nil, }, }, - "NoExternalResourceDeletableIfObjectWasDeleted": { - args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()} - obj.Spec.ManagementPolicy = "ObserveCreateUpdate" - obj.Spec.References = objectReferences() - }), - client: resource.ClientApplicator{ - Client: &test.MockClient{ - MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error { - if key.Name == testReferenceObjectName { - *obj.(*unstructured.Unstructured) = *referenceObject() - return nil - } else if key.Name == externalResourceName { - *obj.(*unstructured.Unstructured) = *externalResource() - return nil - } - return errBoom - }, - MockUpdate: test.NewMockUpdateFn(nil), - }, - }, - }, - want: want{ - out: managed.ExternalObservation{ResourceExists: false}, - err: nil, - }, - }, "ReferenceToObject": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -962,8 +910,8 @@ func Test_helmExternal_Observe(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.References = []v1alpha1.Reference{{}} + mg: kubernetesObject(func(obj *v1alpha2.Object) { + obj.Spec.References = []v1alpha2.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -978,9 +926,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "ConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1alpha1.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -1024,9 +972,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "FailedToGetConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1alpha1.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -1100,7 +1048,7 @@ func Test_helmExternal_Create(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1121,20 +1069,9 @@ func Test_helmExternal_Create(t *testing.T) { err: errors.Wrap(errBoom, errCreateObject), }, }, - "SkipCreateIfManagementPolicyDefined": { - args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.ManagementPolicy = "ObserveDelete" - }), - }, - want: want{ - out: managed.ExternalCreation{}, - err: nil, - }, - }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1219,7 +1156,7 @@ func Test_helmExternal_Update(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1240,20 +1177,9 @@ func Test_helmExternal_Update(t *testing.T) { err: errors.Wrap(errBoom, errApplyObject), }, }, - "SkipUpdateIfManagementPolicyDefined": { - args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.ManagementPolicy = "ObserveDelete" - }), - }, - want: want{ - out: managed.ExternalUpdate{}, - err: nil, - }, - }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1325,7 +1251,7 @@ func Test_helmExternal_Delete(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1346,19 +1272,9 @@ func Test_helmExternal_Delete(t *testing.T) { err: errors.Wrap(errBoom, errDeleteObject), }, }, - "SkipDeleteIfManagementPolicyDefined": { - args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.ManagementPolicy = "ObserveCreateUpdate" - }), - }, - want: want{ - err: nil, - }, - }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1441,7 +1357,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "ObjectFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), }, @@ -1451,7 +1367,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1469,8 +1385,8 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { - obj.Spec.References = []v1alpha1.Reference{{}} + mg: kubernetesObject(func(obj *v1alpha2.Object) { + obj.Spec.References = []v1alpha2.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -1484,7 +1400,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "FailedToAddReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1511,7 +1427,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1563,7 +1479,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveObjectFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), client: resource.ClientApplicator{ @@ -1588,7 +1504,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "NoReferenceFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() }), @@ -1609,7 +1525,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "ReferenceNotFound": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1628,7 +1544,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1658,7 +1574,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1alpha1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1690,7 +1606,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { t.Errorf("f.RemoveFinalizer(...): -want error, +got error: %s", diff) } - if _, ok := tc.args.mg.(*v1alpha1.Object); ok { + if _, ok := tc.args.mg.(*v1alpha2.Object); ok { sort := cmpopts.SortSlices(func(a, b string) bool { return a < b }) if diff := cmp.Diff(tc.want.finalizers, tc.args.mg.GetFinalizers(), sort); diff != "" { t.Errorf("managed resource finalizers: -want, +got: %s", diff) @@ -1716,7 +1632,7 @@ func Test_connectionDetails(t *testing.T) { } } - connDetail := v1alpha1.ConnectionDetail{ + connDetail := v1alpha2.ConnectionDetail{ ObjectReference: corev1.ObjectReference{ Kind: "Secret", Namespace: testNamespace, @@ -1729,7 +1645,7 @@ func Test_connectionDetails(t *testing.T) { type args struct { kube client.Client - connDetails []v1alpha1.ConnectionDetail + connDetails []v1alpha2.ConnectionDetail } type want struct { out managed.ConnectionDetails @@ -1745,7 +1661,7 @@ func Test_connectionDetails(t *testing.T) { map[string]interface{}{}, kerrors.NewNotFound(schema.GroupResource{Group: "", Resource: "secrets"}, testSecretName), ), - connDetails: []v1alpha1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1760,7 +1676,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1775,7 +1691,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1790,7 +1706,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{ @@ -1814,7 +1730,7 @@ func Test_connectionDetails(t *testing.T) { func Test_updateConditionFromObserved(t *testing.T) { type args struct { - obj *v1alpha1.Object + obj *v1alpha2.Object observed *unstructured.Unstructured } type want struct { @@ -1827,7 +1743,7 @@ func Test_updateConditionFromObserved(t *testing.T) { }{ "NoopIfNoPolicyDefined": { args: args{ - obj: &v1alpha1.Object{}, + obj: &v1alpha2.Object{}, observed: &unstructured.Unstructured{ Object: map[string]interface{}{ "status": xpv1.ConditionedStatus{}, @@ -1841,10 +1757,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "NoopIfSuccessfulCreatePolicyDefined": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicySuccessfulCreate, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicySuccessfulCreate, }, }, }, @@ -1861,10 +1777,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDeriveFromObjectAndNotReady": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1894,10 +1810,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndNoCondition": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1920,10 +1836,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "AvailableIfDeriveFromObjectAndReady": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1953,10 +1869,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndCantParse": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index 163e5d01..01856965 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -588,17 +588,6 @@ spec: - '*' type: string type: array - managementPolicy: - default: Default - description: A ManagementPolicy determines what should happen to the - underlying external resource when a managed resource is created, - updated, deleted, or observed. - enum: - - Default - - ObserveCreateUpdate - - ObserveDelete - - Observe - type: string providerConfigRef: default: name: default From d7809ff4b8165e0d09cf83c9faedf95f666cd85e Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 16:24:29 +0300 Subject: [PATCH 05/26] Add conversion config to object CRD Signed-off-by: Hasan Turken --- package/crds/kubernetes.crossplane.io_objects.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index 01856965..277296df 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -17,6 +17,15 @@ spec: plural: objects singular: object scope: Cluster + # TODO(turkenh): Find a way to generate this, currently added manually and will be overridden by generate crds + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1alpha2 + clientConfig: + service: + path: /convert versions: - additionalPrinterColumns: - jsonPath: .spec.forProvider.manifest.kind From a4e2030bba60bde68ee356c772f1c7fbfad60993 Mon Sep 17 00:00:00 2001 From: Hasan Turken Date: Thu, 16 Nov 2023 16:32:45 +0300 Subject: [PATCH 06/26] Use v1beta1 instead of v1alpha2 Looks like we need to have at least v1beta1 for conversion, otherwise getting: invalid: spec.conversion.conversionReviewVersions: Invalid value: []string{"v1alpha2"}: must include at least one of v1, v1beta1 Signed-off-by: Hasan Turken --- apis/kubernetes.go | 4 +- apis/object/v1alpha1/conversion.go | 10 +- .../{v1alpha2 => v1beta1}/conversion.go | 2 +- apis/object/{v1alpha2 => v1beta1}/doc.go | 6 +- apis/object/{v1alpha2 => v1beta1}/register.go | 2 +- apis/object/{v1alpha2 => v1beta1}/types.go | 2 +- .../zz_generated.deepcopy.go | 2 +- .../zz_generated.managed.go | 2 +- .../zz_generated.managedlist.go | 2 +- cmd/provider/main.go | 4 +- internal/controller/object/object.go | 48 +++--- internal/controller/object/object_test.go | 150 +++++++++--------- .../kubernetes.crossplane.io_objects.yaml | 2 +- 13 files changed, 118 insertions(+), 118 deletions(-) rename apis/object/{v1alpha2 => v1beta1}/conversion.go (81%) rename apis/object/{v1alpha2 => v1beta1}/doc.go (82%) rename apis/object/{v1alpha2 => v1beta1}/register.go (98%) rename apis/object/{v1alpha2 => v1beta1}/types.go (99%) rename apis/object/{v1alpha2 => v1beta1}/zz_generated.deepcopy.go (99%) rename apis/object/{v1alpha2 => v1beta1}/zz_generated.managed.go (99%) rename apis/object/{v1alpha2 => v1beta1}/zz_generated.managedlist.go (98%) diff --git a/apis/kubernetes.go b/apis/kubernetes.go index cf9a024e..ed9858a2 100644 --- a/apis/kubernetes.go +++ b/apis/kubernetes.go @@ -20,7 +20,7 @@ package apis import ( "k8s.io/apimachinery/pkg/runtime" - objectv1alpha2 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + objectv1beta1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -28,7 +28,7 @@ func init() { // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back AddToSchemes = append(AddToSchemes, templatev1alpha1.SchemeBuilder.AddToScheme, - objectv1alpha2.SchemeBuilder.AddToScheme, + objectv1beta1.SchemeBuilder.AddToScheme, ) } diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 5f5d18ff..85989a85 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -7,12 +7,12 @@ import ( xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" ) -// ConvertTo converts this Object to the Hub version (v1alpha2). +// ConvertTo converts this Object to the Hub version (v1beta1). func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. - dst := dstRaw.(*v1alpha2.Object) + dst := dstRaw.(*v1beta1.Object) switch src.Spec.ManagementPolicy { case Default: @@ -30,9 +30,9 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // return nil } -// ConvertFrom converts from the Hub version (v1alpha2) to this version. +// ConvertFrom converts from the Hub version (v1beta1) to this version. func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. - src := srcRaw.(*v1alpha2.Object) + src := srcRaw.(*v1beta1.Object) policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) diff --git a/apis/object/v1alpha2/conversion.go b/apis/object/v1beta1/conversion.go similarity index 81% rename from apis/object/v1alpha2/conversion.go rename to apis/object/v1beta1/conversion.go index 69917e4a..97040e5a 100644 --- a/apis/object/v1alpha2/conversion.go +++ b/apis/object/v1beta1/conversion.go @@ -1,4 +1,4 @@ -package v1alpha2 +package v1beta1 // Hub marks this type as a conversion hub. func (g *Object) Hub() { diff --git a/apis/object/v1alpha2/doc.go b/apis/object/v1beta1/doc.go similarity index 82% rename from apis/object/v1alpha2/doc.go rename to apis/object/v1beta1/doc.go index 8bee2199..45c9e377 100644 --- a/apis/object/v1alpha2/doc.go +++ b/apis/object/v1beta1/doc.go @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1alpha2 contains the v1alpha1 group Object resources of the Kubernetes provider. +// Package v1beta1 contains the v1beta1 group Object resources of the Kubernetes provider. // +kubebuilder:object:generate=true // +groupName=kubernetes.crossplane.io -// +versionName=v1alpha2 -package v1alpha2 +// +versionName=v1beta1 +package v1beta1 diff --git a/apis/object/v1alpha2/register.go b/apis/object/v1beta1/register.go similarity index 98% rename from apis/object/v1alpha2/register.go rename to apis/object/v1beta1/register.go index c2b92abc..0b518745 100644 --- a/apis/object/v1alpha2/register.go +++ b/apis/object/v1beta1/register.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha2 +package v1beta1 import ( "reflect" diff --git a/apis/object/v1alpha2/types.go b/apis/object/v1beta1/types.go similarity index 99% rename from apis/object/v1alpha2/types.go rename to apis/object/v1beta1/types.go index 7f5f2013..8946ca6b 100644 --- a/apis/object/v1alpha2/types.go +++ b/apis/object/v1beta1/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha2 +package v1beta1 import ( v1 "k8s.io/api/core/v1" diff --git a/apis/object/v1alpha2/zz_generated.deepcopy.go b/apis/object/v1beta1/zz_generated.deepcopy.go similarity index 99% rename from apis/object/v1alpha2/zz_generated.deepcopy.go rename to apis/object/v1beta1/zz_generated.deepcopy.go index c0d78570..35923c28 100644 --- a/apis/object/v1alpha2/zz_generated.deepcopy.go +++ b/apis/object/v1beta1/zz_generated.deepcopy.go @@ -18,7 +18,7 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1alpha2 +package v1beta1 import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/apis/object/v1alpha2/zz_generated.managed.go b/apis/object/v1beta1/zz_generated.managed.go similarity index 99% rename from apis/object/v1alpha2/zz_generated.managed.go rename to apis/object/v1beta1/zz_generated.managed.go index 9ed35007..f37ee992 100644 --- a/apis/object/v1alpha2/zz_generated.managed.go +++ b/apis/object/v1beta1/zz_generated.managed.go @@ -15,7 +15,7 @@ limitations under the License. */ // Code generated by angryjet. DO NOT EDIT. -package v1alpha2 +package v1beta1 import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" diff --git a/apis/object/v1alpha2/zz_generated.managedlist.go b/apis/object/v1beta1/zz_generated.managedlist.go similarity index 98% rename from apis/object/v1alpha2/zz_generated.managedlist.go rename to apis/object/v1beta1/zz_generated.managedlist.go index b5000727..56302560 100644 --- a/apis/object/v1alpha2/zz_generated.managedlist.go +++ b/apis/object/v1beta1/zz_generated.managedlist.go @@ -15,7 +15,7 @@ limitations under the License. */ // Code generated by angryjet. DO NOT EDIT. -package v1alpha2 +package v1beta1 import resource "github.com/crossplane/crossplane-runtime/pkg/resource" diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 71f2e8de..f85ee784 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -36,7 +36,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" "github.com/crossplane-contrib/provider-kubernetes/apis" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" object "github.com/crossplane-contrib/provider-kubernetes/internal/controller" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -99,7 +99,7 @@ func main() { Features: &feature.Flags{}, } - kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1alpha2.Object{}).Complete(), "Cannot create Object webhook") + kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1beta1.Object{}).Complete(), "Cannot create Object webhook") kingpin.FatalIfError(object.Setup(mgr, o), "Cannot setup controller") kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index c4423999..da23b27c 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -43,7 +43,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" apisv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" "github.com/crossplane-contrib/provider-kubernetes/internal/clients" "github.com/crossplane-contrib/provider-kubernetes/internal/clients/azure" @@ -89,12 +89,12 @@ const ( // Setup adds a controller that reconciles Object managed resources. func Setup(mgr ctrl.Manager, o controller.Options) error { - name := managed.ControllerName(v1alpha2.ObjectGroupKind) + name := managed.ControllerName(v1beta1.ObjectGroupKind) cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} r := managed.NewReconciler(mgr, - resource.ManagedKind(v1alpha2.ObjectGroupVersionKind), + resource.ManagedKind(v1beta1.ObjectGroupVersionKind), managed.WithExternalConnecter(&connector{ logger: o.Logger, kube: mgr.GetClient(), @@ -116,7 +116,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { return ctrl.NewControllerManagedBy(mgr). Named(name). WithOptions(o.ForControllerRuntime()). - For(&v1alpha2.Object{}). + For(&v1beta1.Object{}). Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) } @@ -138,7 +138,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E // This method is currently a little over our complexity goal - be wary // of making it more complex. - cr, ok := mg.(*v1alpha2.Object) + cr, ok := mg.(*v1beta1.Object) if !ok { return nil, errors.New(errNotKubernetesObject) } @@ -233,7 +233,7 @@ type external struct { } func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { - cr, ok := mg.(*v1alpha2.Object) + cr, ok := mg.(*v1beta1.Object) if !ok { return managed.ExternalObservation{}, errors.New(errNotKubernetesObject) } @@ -276,7 +276,7 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex } func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { - cr, ok := mg.(*v1alpha2.Object) + cr, ok := mg.(*v1beta1.Object) if !ok { return managed.ExternalCreation{}, errors.New(errNotKubernetesObject) } @@ -300,7 +300,7 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { - cr, ok := mg.(*v1alpha2.Object) + cr, ok := mg.(*v1beta1.Object) if !ok { return managed.ExternalUpdate{}, errors.New(errNotKubernetesObject) } @@ -324,7 +324,7 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Delete(ctx context.Context, mg resource.Managed) error { - cr, ok := mg.(*v1alpha2.Object) + cr, ok := mg.(*v1beta1.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -339,7 +339,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error { return errors.Wrap(resource.IgnoreNotFound(c.client.Delete(ctx, obj)), errDeleteObject) } -func getDesired(obj *v1alpha2.Object) (*unstructured.Unstructured, error) { +func getDesired(obj *v1beta1.Object) (*unstructured.Unstructured, error) { desired := &unstructured.Unstructured{} if err := json.Unmarshal(obj.Spec.ForProvider.Manifest.Raw, desired); err != nil { return nil, errors.Wrap(err, errUnmarshalTemplate) @@ -351,7 +351,7 @@ func getDesired(obj *v1alpha2.Object) (*unstructured.Unstructured, error) { return desired, nil } -func getLastApplied(obj *v1alpha2.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { +func getLastApplied(obj *v1beta1.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { lastApplied, ok := observed.GetAnnotations()[v1.LastAppliedConfigAnnotation] if !ok { return nil, nil @@ -369,7 +369,7 @@ func getLastApplied(obj *v1alpha2.Object, observed *unstructured.Unstructured) ( return last, nil } -func (c *external) setObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { +func (c *external) setObserved(obj *v1beta1.Object, observed *unstructured.Unstructured) error { var err error if obj.Status.AtProvider.Manifest.Raw, err = observed.MarshalJSON(); err != nil { return errors.Wrap(err, errFailedToMarshalExisting) @@ -381,9 +381,9 @@ func (c *external) setObserved(obj *v1alpha2.Object, observed *unstructured.Unst return nil } -func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { +func (c *external) updateConditionFromObserved(obj *v1beta1.Object, observed *unstructured.Unstructured) error { switch obj.Spec.Readiness.Policy { - case v1alpha2.ReadinessPolicyDeriveFromObject: + case v1beta1.ReadinessPolicyDeriveFromObject: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -397,7 +397,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *u return nil } obj.SetConditions(xpv1.Available()) - case v1alpha2.ReadinessPolicyAllTrue: + case v1beta1.ReadinessPolicyAllTrue: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -417,7 +417,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *u } else { obj.SetConditions(xpv1.Unavailable()) } - case v1alpha2.ReadinessPolicySuccessfulCreate, "": + case v1beta1.ReadinessPolicySuccessfulCreate, "": // do nothing, will be handled by c.handleLastApplied method // "" should never happen, but just in case we will treat it as SuccessfulCreate for backward compatibility default: @@ -427,7 +427,7 @@ func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *u return nil } -func getReferenceInfo(ref v1alpha2.Reference) (string, string, string, string) { +func getReferenceInfo(ref v1beta1.Reference) (string, string, string, string) { var apiVersion, kind, namespace, name string if ref.PatchesFrom != nil { @@ -450,7 +450,7 @@ func getReferenceInfo(ref v1alpha2.Reference) (string, string, string, string) { // resolveReferencies resolves references for the current Object. If it fails to // resolve some reference, e.g.: due to reference not ready, it will then return // error and requeue to wait for resolving it next time. -func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) error { +func (c *external) resolveReferencies(ctx context.Context, obj *v1beta1.Object) error { c.logger.Debug("Resolving referencies.") // Loop through references to resolve each referenced resource @@ -484,7 +484,7 @@ func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) return nil } -func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { +func (c *external) handleLastApplied(ctx context.Context, obj *v1beta1.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { isUpToDate := false if last != nil && equality.Semantic.DeepEqual(last, desired) { @@ -495,7 +495,7 @@ func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, if isUpToDate { c.logger.Debug("Up to date!") - if p := obj.Spec.Readiness.Policy; p == v1alpha2.ReadinessPolicySuccessfulCreate || p == "" { + if p := obj.Spec.Readiness.Policy; p == v1beta1.ReadinessPolicySuccessfulCreate || p == "" { obj.Status.SetConditions(xpv1.Available()) } @@ -524,7 +524,7 @@ type objFinalizer struct { type refFinalizerFn func(context.Context, *unstructured.Unstructured, string) error -func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha2.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { +func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1beta1.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { // Loop through references to resolve each referenced resource for _, ref := range obj.Spec.References { if ref.DependsOn == nil && ref.PatchesFrom == nil { @@ -560,7 +560,7 @@ func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha2.Obj } func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1alpha2.Object) + obj, ok := res.(*v1beta1.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -590,7 +590,7 @@ func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) er } func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1alpha2.Object) + obj, ok := res.(*v1beta1.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -619,7 +619,7 @@ func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) return errors.Wrap(err, errRemoveFinalizer) } -func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1alpha2.ConnectionDetail) (managed.ConnectionDetails, error) { +func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1beta1.ConnectionDetail) (managed.ConnectionDetails, error) { mcd := managed.ConnectionDetails{} for _, cd := range connDetails { diff --git a/internal/controller/object/object_test.go b/internal/controller/object/object_test.go index 0ba4c81c..d0c9c3cd 100644 --- a/internal/controller/object/object_test.go +++ b/internal/controller/object/object_test.go @@ -42,7 +42,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" kubernetesv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -80,30 +80,30 @@ type notKubernetesObject struct { resource.Managed } -type kubernetesObjectModifier func(obj *v1alpha2.Object) +type kubernetesObjectModifier func(obj *v1beta1.Object) type externalResourceModifier func(res *unstructured.Unstructured) -func kubernetesObject(om ...kubernetesObjectModifier) *v1alpha2.Object { - o := &v1alpha2.Object{ +func kubernetesObject(om ...kubernetesObjectModifier) *v1beta1.Object { + o := &v1beta1.Object{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1alpha2.SchemeGroupVersion.String(), - Kind: v1alpha2.ObjectKind, + APIVersion: v1beta1.SchemeGroupVersion.String(), + Kind: v1beta1.ObjectKind, }, ObjectMeta: metav1.ObjectMeta{ Name: testObjectName, Namespace: testNamespace, }, - Spec: v1alpha2.ObjectSpec{ + Spec: v1beta1.ObjectSpec{ ResourceSpec: xpv1.ResourceSpec{ ProviderConfigReference: &xpv1.Reference{ Name: providerName, }, }, - ForProvider: v1alpha2.ObjectParameters{ + ForProvider: v1beta1.ObjectParameters{ Manifest: runtime.RawExtension{Raw: externalResourceRaw}, }, }, - Status: v1alpha2.ObjectStatus{}, + Status: v1beta1.ObjectStatus{}, } for _, m := range om { @@ -145,16 +145,16 @@ func upToDateExternalResource() *unstructured.Unstructured { return externalResourceWithLastAppliedConfigAnnotation(string(externalResourceRaw)) } -func objectReferences() []v1alpha2.Reference { - dependsOn := v1alpha2.DependsOn{ - APIVersion: v1alpha2.SchemeGroupVersion.String(), - Kind: v1alpha2.ObjectKind, +func objectReferences() []v1beta1.Reference { + dependsOn := v1beta1.DependsOn{ + APIVersion: v1beta1.SchemeGroupVersion.String(), + Kind: v1beta1.ObjectKind, Name: testReferenceObjectName, Namespace: testNamespace, } - ref := []v1alpha2.Reference{ + ref := []v1beta1.Reference{ { - PatchesFrom: &v1alpha2.PatchesFrom{ + PatchesFrom: &v1beta1.PatchesFrom{ DependsOn: dependsOn, }, }, @@ -168,8 +168,8 @@ func objectReferences() []v1alpha2.Reference { func referenceObject(rm ...externalResourceModifier) *unstructured.Unstructured { obj := &unstructured.Unstructured{ Object: map[string]interface{}{ - "apiVersion": v1alpha2.SchemeGroupVersion.String(), - "kind": v1alpha2.ObjectKind, + "apiVersion": v1beta1.SchemeGroupVersion.String(), + "kind": v1beta1.ObjectKind, "metadata": map[string]interface{}{ "name": testReferenceObjectName, "namespace": testNamespace, @@ -707,7 +707,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -767,7 +767,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "UpToDateNameDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -816,7 +816,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "FailedToPatchFieldFromReferenceObject": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() obj.Spec.References[0].PatchesFrom.FieldPath = pointer.String("nonexistent_field") }), @@ -837,7 +837,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -854,7 +854,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoExternalResourceExistsIfObjectWasDeleted": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()} obj.Spec.References = objectReferences() }), @@ -880,7 +880,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "ReferenceToObject": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -910,8 +910,8 @@ func Test_helmExternal_Observe(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { - obj.Spec.References = []v1alpha2.Reference{{}} + mg: kubernetesObject(func(obj *v1beta1.Object) { + obj.Spec.References = []v1beta1.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -926,9 +926,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "ConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1beta1.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -972,9 +972,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "FailedToGetConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1beta1.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -1048,7 +1048,7 @@ func Test_helmExternal_Create(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1071,7 +1071,7 @@ func Test_helmExternal_Create(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1156,7 +1156,7 @@ func Test_helmExternal_Update(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1179,7 +1179,7 @@ func Test_helmExternal_Update(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1251,7 +1251,7 @@ func Test_helmExternal_Delete(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1274,7 +1274,7 @@ func Test_helmExternal_Delete(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1357,7 +1357,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "ObjectFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), }, @@ -1367,7 +1367,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1385,8 +1385,8 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { - obj.Spec.References = []v1alpha2.Reference{{}} + mg: kubernetesObject(func(obj *v1beta1.Object) { + obj.Spec.References = []v1beta1.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -1400,7 +1400,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "FailedToAddReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1427,7 +1427,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1479,7 +1479,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveObjectFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), client: resource.ClientApplicator{ @@ -1504,7 +1504,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "NoReferenceFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() }), @@ -1525,7 +1525,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "ReferenceNotFound": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1544,7 +1544,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1574,7 +1574,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1alpha2.Object) { + mg: kubernetesObject(func(obj *v1beta1.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1606,7 +1606,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { t.Errorf("f.RemoveFinalizer(...): -want error, +got error: %s", diff) } - if _, ok := tc.args.mg.(*v1alpha2.Object); ok { + if _, ok := tc.args.mg.(*v1beta1.Object); ok { sort := cmpopts.SortSlices(func(a, b string) bool { return a < b }) if diff := cmp.Diff(tc.want.finalizers, tc.args.mg.GetFinalizers(), sort); diff != "" { t.Errorf("managed resource finalizers: -want, +got: %s", diff) @@ -1632,7 +1632,7 @@ func Test_connectionDetails(t *testing.T) { } } - connDetail := v1alpha2.ConnectionDetail{ + connDetail := v1beta1.ConnectionDetail{ ObjectReference: corev1.ObjectReference{ Kind: "Secret", Namespace: testNamespace, @@ -1645,7 +1645,7 @@ func Test_connectionDetails(t *testing.T) { type args struct { kube client.Client - connDetails []v1alpha2.ConnectionDetail + connDetails []v1beta1.ConnectionDetail } type want struct { out managed.ConnectionDetails @@ -1661,7 +1661,7 @@ func Test_connectionDetails(t *testing.T) { map[string]interface{}{}, kerrors.NewNotFound(schema.GroupResource{Group: "", Resource: "secrets"}, testSecretName), ), - connDetails: []v1alpha2.ConnectionDetail{connDetail}, + connDetails: []v1beta1.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1676,7 +1676,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha2.ConnectionDetail{connDetail}, + connDetails: []v1beta1.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1691,7 +1691,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha2.ConnectionDetail{connDetail}, + connDetails: []v1beta1.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1706,7 +1706,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1alpha2.ConnectionDetail{connDetail}, + connDetails: []v1beta1.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{ @@ -1730,7 +1730,7 @@ func Test_connectionDetails(t *testing.T) { func Test_updateConditionFromObserved(t *testing.T) { type args struct { - obj *v1alpha2.Object + obj *v1beta1.Object observed *unstructured.Unstructured } type want struct { @@ -1743,7 +1743,7 @@ func Test_updateConditionFromObserved(t *testing.T) { }{ "NoopIfNoPolicyDefined": { args: args{ - obj: &v1alpha2.Object{}, + obj: &v1beta1.Object{}, observed: &unstructured.Unstructured{ Object: map[string]interface{}{ "status": xpv1.ConditionedStatus{}, @@ -1757,10 +1757,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "NoopIfSuccessfulCreatePolicyDefined": { args: args{ - obj: &v1alpha2.Object{ - Spec: v1alpha2.ObjectSpec{ - Readiness: v1alpha2.Readiness{ - Policy: v1alpha2.ReadinessPolicySuccessfulCreate, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicySuccessfulCreate, }, }, }, @@ -1777,10 +1777,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDeriveFromObjectAndNotReady": { args: args{ - obj: &v1alpha2.Object{ - Spec: v1alpha2.ObjectSpec{ - Readiness: v1alpha2.Readiness{ - Policy: v1alpha2.ReadinessPolicyDeriveFromObject, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1810,10 +1810,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndNoCondition": { args: args{ - obj: &v1alpha2.Object{ - Spec: v1alpha2.ObjectSpec{ - Readiness: v1alpha2.Readiness{ - Policy: v1alpha2.ReadinessPolicyDeriveFromObject, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1836,10 +1836,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "AvailableIfDeriveFromObjectAndReady": { args: args{ - obj: &v1alpha2.Object{ - Spec: v1alpha2.ObjectSpec{ - Readiness: v1alpha2.Readiness{ - Policy: v1alpha2.ReadinessPolicyDeriveFromObject, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1869,10 +1869,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndCantParse": { args: args{ - obj: &v1alpha2.Object{ - Spec: v1alpha2.ObjectSpec{ - Readiness: v1alpha2.Readiness{ - Policy: v1alpha2.ReadinessPolicyDeriveFromObject, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyDeriveFromObject, }, }, }, diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index 277296df..c572c456 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -22,7 +22,7 @@ spec: strategy: Webhook webhook: conversionReviewVersions: - - v1alpha2 + - v1beta1 clientConfig: service: path: /convert From ce2c5c40214c3546dd8465f99d88c14194ef7123 Mon Sep 17 00:00:00 2001 From: lsviben Date: Fri, 17 Nov 2023 13:07:47 +0100 Subject: [PATCH 07/26] rename leftover v1alpha2 to v1beta1 Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 16 +++++++++++ apis/object/v1beta1/conversion.go | 16 +++++++++++ apis/object/v1beta1/doc.go | 2 +- apis/object/v1beta1/register.go | 4 +-- apis/object/v1beta1/types.go | 27 +------------------ .../kubernetes.crossplane.io_objects.yaml | 2 +- 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 85989a85..4c98d81d 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 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 ( diff --git a/apis/object/v1beta1/conversion.go b/apis/object/v1beta1/conversion.go index 97040e5a..d2ad6790 100644 --- a/apis/object/v1beta1/conversion.go +++ b/apis/object/v1beta1/conversion.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 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 v1beta1 // Hub marks this type as a conversion hub. diff --git a/apis/object/v1beta1/doc.go b/apis/object/v1beta1/doc.go index 45c9e377..e55ae3c6 100644 --- a/apis/object/v1beta1/doc.go +++ b/apis/object/v1beta1/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Crossplane Authors. +Copyright 2023 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. diff --git a/apis/object/v1beta1/register.go b/apis/object/v1beta1/register.go index 0b518745..87fc1f7e 100644 --- a/apis/object/v1beta1/register.go +++ b/apis/object/v1beta1/register.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Crossplane Authors. +Copyright 2023 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. @@ -26,7 +26,7 @@ import ( // Package type metadata. const ( Group = "kubernetes.crossplane.io" - Version = "v1alpha1" + Version = "v1beta1" ) var ( diff --git a/apis/object/v1beta1/types.go b/apis/object/v1beta1/types.go index 8946ca6b..b5e49161 100644 --- a/apis/object/v1beta1/types.go +++ b/apis/object/v1beta1/types.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Crossplane Authors. +Copyright 2023 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. @@ -28,31 +28,6 @@ import ( // 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. diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index c572c456..a61df92f 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -485,7 +485,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date - name: v1alpha2 + name: v1beta1 schema: openAPIV3Schema: description: A Object is an provider Kubernetes API type From 74005619415cda45906847d720ef77fe8e29aca5 Mon Sep 17 00:00:00 2001 From: lsviben Date: Fri, 17 Nov 2023 13:08:35 +0100 Subject: [PATCH 08/26] add .idea to gitignore Signed-off-by: lsviben --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 22634d71..ccfd4107 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ /bin /vendor /.vendor-new -.vscode \ No newline at end of file +.vscode +/.idea + From 5323a525c5043f7a0c36d96064456cae9acd4b7d Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 20 Nov 2023 16:25:00 +0100 Subject: [PATCH 09/26] set the webhook to convert alpha versions Signed-off-by: lsviben --- apis/kubernetes.go | 2 ++ apis/object/v1alpha1/conversion.go | 10 +++++++++- cmd/provider/main.go | 6 +++--- package/crds/kubernetes.crossplane.io_objects.yaml | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apis/kubernetes.go b/apis/kubernetes.go index ed9858a2..cbc0369e 100644 --- a/apis/kubernetes.go +++ b/apis/kubernetes.go @@ -20,6 +20,7 @@ package apis import ( "k8s.io/apimachinery/pkg/runtime" + objectv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" objectv1beta1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -28,6 +29,7 @@ func init() { // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back AddToSchemes = append(AddToSchemes, templatev1alpha1.SchemeBuilder.AddToScheme, + objectv1alpha1.SchemeBuilder.AddToScheme, objectv1beta1.SchemeBuilder.AddToScheme, ) } diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 4c98d81d..de6fea3f 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -30,6 +30,10 @@ import ( func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. dst := dstRaw.(*v1beta1.Object) + // copy identical fields + dst.ObjectMeta = src.ObjectMeta + + // handle management policies migration switch src.Spec.ManagementPolicy { case Default: dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionAll} @@ -50,8 +54,12 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. src := srcRaw.(*v1beta1.Object) - policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) + // copy identical fields + dst.ObjectMeta = src.ObjectMeta + // handle management policies migration + policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) + switch { case policySet.Has(xpv1.ManagementActionAll): dst.Spec.ManagementPolicy = Default diff --git a/cmd/provider/main.go b/cmd/provider/main.go index f85ee784..69b75bbc 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -36,7 +36,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" "github.com/crossplane-contrib/provider-kubernetes/apis" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" object "github.com/crossplane-contrib/provider-kubernetes/internal/controller" _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -85,7 +85,7 @@ func main() { LeaseDuration: func() *time.Duration { d := 60 * time.Second; return &d }(), RenewDeadline: func() *time.Duration { d := 50 * time.Second; return &d }(), WebhookServer: webhook.NewServer(webhook.Options{ - CertDir: filepath.Join("/", "webhook", "tls"), + CertDir: filepath.Join("/", "tls", "server"), }), }) kingpin.FatalIfError(err, "Cannot create controller manager") @@ -99,7 +99,7 @@ func main() { Features: &feature.Flags{}, } - kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1beta1.Object{}).Complete(), "Cannot create Object webhook") + kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1alpha1.Object{}).Complete(), "Cannot create Object webhook") kingpin.FatalIfError(object.Setup(mgr, o), "Cannot setup controller") kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index a61df92f..76fba29c 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -22,6 +22,7 @@ spec: strategy: Webhook webhook: conversionReviewVersions: + - v1alpha1 - v1beta1 clientConfig: service: From 2cf19986364cf2994297fd27d7c38cda10bda4d1 Mon Sep 17 00:00:00 2001 From: lsviben Date: Tue, 28 Nov 2023 13:40:29 +0100 Subject: [PATCH 10/26] add full conversion between object versions Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 106 ++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index de6fea3f..bd9cbc21 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/conversion" @@ -33,6 +34,58 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // // copy identical fields dst.ObjectMeta = src.ObjectMeta + dst.Status = v1beta1.ObjectStatus{ + ResourceStatus: src.Status.ResourceStatus, + AtProvider: v1beta1.ObjectObservation{ + Manifest: src.Status.AtProvider.Manifest, + }, + } + + connectionDetails := []v1beta1.ConnectionDetail{} + for _, cd := range src.Spec.ConnectionDetails { + connectionDetails = append(connectionDetails, v1beta1.ConnectionDetail{ + ObjectReference: cd.ObjectReference, + }) + } + + references := []v1beta1.Reference{} + for _, r := range src.Spec.References { + references = append(references, v1beta1.Reference{ + DependsOn: &v1beta1.DependsOn{ + APIVersion: r.DependsOn.APIVersion, + Kind: r.DependsOn.Kind, + Name: r.DependsOn.Name, + Namespace: r.DependsOn.Namespace, + }, + PatchesFrom: &v1beta1.PatchesFrom{ + DependsOn: v1beta1.DependsOn{ + APIVersion: r.PatchesFrom.APIVersion, + Kind: r.PatchesFrom.Kind, + Name: r.PatchesFrom.Name, + Namespace: r.PatchesFrom.Namespace, + }, + FieldPath: r.PatchesFrom.FieldPath, + }, + }) + } + + dst.Spec = v1beta1.ObjectSpec{ + ResourceSpec: xpv1.ResourceSpec{ + WriteConnectionSecretToReference: src.GetWriteConnectionSecretToReference(), + PublishConnectionDetailsTo: src.GetPublishConnectionDetailsTo(), + ProviderConfigReference: src.GetProviderConfigReference(), + DeletionPolicy: src.GetDeletionPolicy(), + }, + ConnectionDetails: connectionDetails, + ForProvider: v1beta1.ObjectParameters{ + Manifest: src.Spec.ForProvider.Manifest, + }, + References: references, + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicy(src.Spec.Readiness.Policy), + }, + } + // handle management policies migration switch src.Spec.ManagementPolicy { case Default: @@ -56,10 +109,61 @@ func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint / // copy identical fields dst.ObjectMeta = src.ObjectMeta + dst.Status = ObjectStatus{ + ResourceStatus: src.Status.ResourceStatus, + AtProvider: ObjectObservation{ + Manifest: src.Status.AtProvider.Manifest, + }, + } + + connectionDetails := []ConnectionDetail{} + for _, cd := range src.Spec.ConnectionDetails { + connectionDetails = append(connectionDetails, ConnectionDetail{ + ObjectReference: cd.ObjectReference, + }) + } + + references := []Reference{} + for _, r := range src.Spec.References { + references = append(references, Reference{ + DependsOn: &DependsOn{ + APIVersion: r.DependsOn.APIVersion, + Kind: r.DependsOn.Kind, + Name: r.DependsOn.Name, + Namespace: r.DependsOn.Namespace, + }, + PatchesFrom: &PatchesFrom{ + DependsOn: DependsOn{ + APIVersion: r.PatchesFrom.APIVersion, + Kind: r.PatchesFrom.Kind, + Name: r.PatchesFrom.Name, + Namespace: r.PatchesFrom.Namespace, + }, + FieldPath: r.PatchesFrom.FieldPath, + }, + }) + } + + dst.Spec = ObjectSpec{ + ResourceSpec: ResourceSpec{ + WriteConnectionSecretToReference: src.GetWriteConnectionSecretToReference(), + PublishConnectionDetailsTo: src.GetPublishConnectionDetailsTo(), + ProviderConfigReference: src.GetProviderConfigReference(), + DeletionPolicy: src.GetDeletionPolicy(), + }, + ConnectionDetails: connectionDetails, + ForProvider: ObjectParameters{ + Manifest: src.Spec.ForProvider.Manifest, + }, + References: references, + Readiness: Readiness{ + Policy: ReadinessPolicy(src.Spec.Readiness.Policy), + }, + } // handle management policies migration policySet := sets.New[xpv1.ManagementAction](src.GetManagementPolicies()...) - + switch { case policySet.Has(xpv1.ManagementActionAll): dst.Spec.ManagementPolicy = Default From 3b7beccde12d1980759faef38dc3c66bc398111d Mon Sep 17 00:00:00 2001 From: lsviben Date: Tue, 28 Nov 2023 15:13:24 +0100 Subject: [PATCH 11/26] enable managementpolicies Signed-off-by: lsviben --- cmd/provider/main.go | 18 ++++++++++++------ internal/controller/object/object.go | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/cmd/provider/main.go b/cmd/provider/main.go index 69b75bbc..a071fb57 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -44,12 +44,13 @@ import ( func main() { var ( - app = kingpin.New(filepath.Base(os.Args[0]), "Template support for Crossplane.").DefaultEnvars() - debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() - syncInterval = app.Flag("sync", "Controller manager sync period such as 300ms, 1.5h, or 2h45m").Short('s').Default("1h").Duration() - pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("1m").Duration() - leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").Envar("LEADER_ELECTION").Bool() - maxReconcileRate = app.Flag("max-reconcile-rate", "The number of concurrent reconciliations that may be running at one time.").Default("10").Int() + app = kingpin.New(filepath.Base(os.Args[0]), "Template support for Crossplane.").DefaultEnvars() + debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() + syncInterval = app.Flag("sync", "Controller manager sync period such as 300ms, 1.5h, or 2h45m").Short('s').Default("1h").Duration() + pollInterval = app.Flag("poll", "Poll interval controls how often an individual resource should be checked for drift.").Default("1m").Duration() + leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").Envar("LEADER_ELECTION").Bool() + maxReconcileRate = app.Flag("max-reconcile-rate", "The number of concurrent reconciliations that may be running at one time.").Default("10").Int() + enableManagementPolicies = app.Flag("enable-management-policies", "Enable support for Management Policies.").Default("true").Envar("ENABLE_MANAGEMENT_POLICIES").Bool() ) kingpin.MustParse(app.Parse(os.Args[1:])) @@ -99,6 +100,11 @@ func main() { Features: &feature.Flags{}, } + if *enableManagementPolicies { + o.Features.Enable(feature.EnableBetaManagementPolicies) + log.Info("Beta feature enabled", "flag", feature.EnableBetaManagementPolicies) + } + kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1alpha1.Object{}).Complete(), "Cannot create Object webhook") kingpin.FatalIfError(object.Setup(mgr, o), "Cannot setup controller") diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index da23b27c..f42578e8 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -36,6 +36,7 @@ import ( xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/controller" "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/feature" "github.com/crossplane/crossplane-runtime/pkg/fieldpath" "github.com/crossplane/crossplane-runtime/pkg/logging" "github.com/crossplane/crossplane-runtime/pkg/meta" @@ -93,8 +94,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} - r := managed.NewReconciler(mgr, - resource.ManagedKind(v1beta1.ObjectGroupVersionKind), + reconcilerOptions := []managed.ReconcilerOption{ managed.WithExternalConnecter(&connector{ logger: o.Logger, kube: mgr.GetClient(), @@ -111,7 +111,16 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { managed.WithPollInterval(o.PollInterval), managed.WithLogger(o.Logger.WithValues("controller", name)), managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), - managed.WithConnectionPublishers(cps...)) + managed.WithConnectionPublishers(cps...), + } + + if o.Features.Enabled(feature.EnableBetaManagementPolicies) { + reconcilerOptions = append(reconcilerOptions, managed.WithManagementPolicies()) + } + + r := managed.NewReconciler(mgr, + resource.ManagedKind(v1beta1.ObjectGroupVersionKind), + reconcilerOptions...) return ctrl.NewControllerManagedBy(mgr). Named(name). From 272eb339b7d18b52ce1718b8a07c6aa0c05070e1 Mon Sep 17 00:00:00 2001 From: lsviben Date: Tue, 28 Nov 2023 15:32:13 +0100 Subject: [PATCH 12/26] fix lint Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 2 +- internal/controller/object/object.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index bd9cbc21..9db75a0e 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -104,7 +104,7 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // } // ConvertFrom converts from the Hub version (v1beta1) to this version. -func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. +func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint, gocyclo // We want to use different names for receiver parameter to be more clear. src := srcRaw.(*v1beta1.Object) // copy identical fields diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index f42578e8..c6282c14 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -93,7 +93,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { name := managed.ControllerName(v1beta1.ObjectGroupKind) cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} - + reconcilerOptions := []managed.ReconcilerOption{ managed.WithExternalConnecter(&connector{ logger: o.Logger, From e694985c017f4aeb362f0db21056976d73adadcc Mon Sep 17 00:00:00 2001 From: lsviben Date: Thu, 30 Nov 2023 12:36:34 +0100 Subject: [PATCH 13/26] kustomization for adding conversion webhook Signed-off-by: lsviben --- Makefile | 17 ++++++++++++++++ apis/generate.go | 2 +- cluster/kustomize/kustomization.yaml | 4 ++++ cluster/kustomize/webhook/webhook.patch.yaml | 14 +++++++++++++ .../kubernetes.crossplane.io_objects.yaml | 20 +++++++++---------- 5 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 cluster/kustomize/kustomization.yaml create mode 100644 cluster/kustomize/webhook/webhook.patch.yaml diff --git a/Makefile b/Makefile index bc8c3c2e..417e803b 100644 --- a/Makefile +++ b/Makefile @@ -125,3 +125,20 @@ manifests: @$(INFO) Deprecated. Run make generate instead. .PHONY: cobertura submodules fallthrough test-integration run manifests + +generate.run: kustomize.gen + +generate.done: kustomize.clean + +kustomize.gen: + @$(INFO) Generating CRDs with kustomize + @$(KUBECTL) kustomize cluster/kustomize/ > cluster/kustomize/kubernetes.crossplane.io_objects.yaml + @mv cluster/kustomize/kubernetes.crossplane.io_objects.yaml cluster/kustomize/crds/kubernetes.crossplane.io_objects.yaml + @mv cluster/kustomize/crds package/crds + @$(OK) Generated CRDs with kustomize + +kustomize.clean: + @$(INFO) Cleaning up kustomize generated CRDs + @rm -rf cluster/kustomize/crds + @rm -f cluster/kustomize/kubernetes.crossplane.io_objects.yaml + @$(OK) Cleaned up kustomize generated CRDs diff --git a/apis/generate.go b/apis/generate.go index 8dc4f86a..5a4b0a8f 100644 --- a/apis/generate.go +++ b/apis/generate.go @@ -24,7 +24,7 @@ limitations under the License. //go:generate rm -rf ../package/crds // Generate deepcopy methodsets and CRD manifests -//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../package/crds +//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../cluster/kustomize/crds // Generate crossplane-runtime methodsets (resource.Claim, etc) //go:generate go run -tags generate github.com/crossplane/crossplane-tools/cmd/angryjet generate-methodsets --header-file=../hack/boilerplate.go.txt ./... diff --git a/cluster/kustomize/kustomization.yaml b/cluster/kustomize/kustomization.yaml new file mode 100644 index 00000000..ac63720b --- /dev/null +++ b/cluster/kustomize/kustomization.yaml @@ -0,0 +1,4 @@ +resources: + - crds/kubernetes.crossplane.io_objects.yaml +patches: + - path: webhook/webhook.patch.yaml \ No newline at end of file diff --git a/cluster/kustomize/webhook/webhook.patch.yaml b/cluster/kustomize/webhook/webhook.patch.yaml new file mode 100644 index 00000000..74be3a6f --- /dev/null +++ b/cluster/kustomize/webhook/webhook.patch.yaml @@ -0,0 +1,14 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: objects.kubernetes.crossplane.io +spec: + conversion: + strategy: Webhook + webhook: + conversionReviewVersions: + - v1beta1 + - v1alpha1 + clientConfig: + service: + path: /convert \ No newline at end of file diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index 76fba29c..ef8a086f 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -6,6 +5,15 @@ metadata: controller-gen.kubebuilder.io/version: v0.13.0 name: objects.kubernetes.crossplane.io spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + path: /convert + conversionReviewVersions: + - v1beta1 + - v1alpha1 group: kubernetes.crossplane.io names: categories: @@ -17,16 +25,6 @@ spec: plural: objects singular: object scope: Cluster - # TODO(turkenh): Find a way to generate this, currently added manually and will be overridden by generate crds - conversion: - strategy: Webhook - webhook: - conversionReviewVersions: - - v1alpha1 - - v1beta1 - clientConfig: - service: - path: /convert versions: - additionalPrinterColumns: - jsonPath: .spec.forProvider.manifest.kind From b58c0458842a39646dda3d86f2b524b4b5d63383 Mon Sep 17 00:00:00 2001 From: lsviben Date: Thu, 30 Nov 2023 20:55:58 +0100 Subject: [PATCH 14/26] add conversion unit test Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 2 +- apis/object/v1alpha1/conversion_test.go | 244 ++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 apis/object/v1alpha1/conversion_test.go diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 9db75a0e..fe2efebe 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -17,7 +17,7 @@ limitations under the License. package v1alpha1 import ( - "github.com/pkg/errors" + "errors" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/conversion" diff --git a/apis/object/v1alpha1/conversion_test.go b/apis/object/v1alpha1/conversion_test.go new file mode 100644 index 00000000..5f9ddd1e --- /dev/null +++ b/apis/object/v1alpha1/conversion_test.go @@ -0,0 +1,244 @@ +/* +Copyright 2023 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_test + +import ( + "errors" + "testing" + + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" +) + +func TestConvertTo(t *testing.T) { + type args struct { + src *v1alpha1.Object + } + type want struct { + err error + dst *v1beta1.Object + } + + tests := []struct { + name string + args args + want want + }{ + { + name: "converts to v1beta1", + args: args{ + src: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ResourceSpec: v1alpha1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + }, + ConnectionDetails: []v1alpha1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1alpha1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ManagementPolicy: v1alpha1.Observe, + References: nil, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + want: want{ + dst: &v1beta1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1beta1.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, + }, + ConnectionDetails: []v1beta1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1beta1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + References: []v1beta1.Reference{}, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + }, + { + name: "errors if management policy is unknown", + args: args{ + src: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ManagementPolicy: v1alpha1.ManagementPolicy("unknown"), + }, + }, + }, + want: want{ + err: errors.New("unknown management policy"), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + beta := &v1beta1.Object{} + err := tc.args.src.ConvertTo(beta) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\nr.ConvertTo(...): -want error, +got error:\n%s", diff) + } + if err != nil { + return + } + if diff := cmp.Diff(tc.want.dst, beta); diff != "" { + t.Errorf("\nr.ConvertTo(...): -want converted, +got converted:\n%s", diff) + } + }) + } +} + +func TestConvertFrom(t *testing.T) { + type args struct { + src *v1beta1.Object + } + type want struct { + err error + dst *v1alpha1.Object + } + + tests := []struct { + name string + args args + want want + }{ + { + name: "converts to v1beta1", + args: args{ + src: &v1beta1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1beta1.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, + }, + ConnectionDetails: []v1beta1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1beta1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + References: []v1beta1.Reference{}, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + want: want{ + dst: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ResourceSpec: v1alpha1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + }, + ConnectionDetails: []v1alpha1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1alpha1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ManagementPolicy: v1alpha1.Observe, + References: []v1alpha1.Reference{}, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + }, + { + name: "errors if management policy is unknown", + args: args{ + src: &v1beta1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1beta1.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + ManagementPolicies: []v1.ManagementAction{}, + }, + }, + }, + }, + want: want{ + err: errors.New("unsupported management policy"), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + alpha := &v1alpha1.Object{} + err := alpha.ConvertFrom(tc.args.src) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\nr.ConvertFrom(...): -want error, +got error:\n%s", diff) + } + if err != nil { + return + } + if diff := cmp.Diff(tc.want.dst, alpha); diff != "" { + t.Errorf("\nr.ConvertTo(...): -want converted, +got converted:\n%s", diff) + } + }) + } +} From 84dde7acf6257af65be0d58f6d2038c537ccdcae Mon Sep 17 00:00:00 2001 From: lsviben Date: Thu, 30 Nov 2023 21:22:20 +0100 Subject: [PATCH 15/26] fix Makefile kubectl Signed-off-by: lsviben --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 417e803b..70b2bf3a 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ generate.run: kustomize.gen generate.done: kustomize.clean -kustomize.gen: +kustomize.gen: $(KUBECTL) @$(INFO) Generating CRDs with kustomize @$(KUBECTL) kustomize cluster/kustomize/ > cluster/kustomize/kubernetes.crossplane.io_objects.yaml @mv cluster/kustomize/kubernetes.crossplane.io_objects.yaml cluster/kustomize/crds/kubernetes.crossplane.io_objects.yaml From f1b4a2bdc5c12e418b0c11f4bf804afcdb3b57cd Mon Sep 17 00:00:00 2001 From: lsviben Date: Fri, 1 Dec 2023 12:12:31 +0100 Subject: [PATCH 16/26] fix lint Signed-off-by: lsviben --- apis/object/v1alpha1/conversion_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apis/object/v1alpha1/conversion_test.go b/apis/object/v1alpha1/conversion_test.go index 5f9ddd1e..21c7090f 100644 --- a/apis/object/v1alpha1/conversion_test.go +++ b/apis/object/v1alpha1/conversion_test.go @@ -20,13 +20,14 @@ import ( "errors" "testing" - v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" - "github.com/crossplane/crossplane-runtime/pkg/test" "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" ) From 4ae391cb2506ae5b6568a1d3f56767463d307631 Mon Sep 17 00:00:00 2001 From: lsviben Date: Fri, 1 Dec 2023 12:37:09 +0100 Subject: [PATCH 17/26] update examples Signed-off-by: lsviben --- examples/in-composition/composition.yaml | 4 +-- examples/object/migration/default.yaml | 17 ++++++++++++ .../migration/observe-create-update.yaml | 21 +++++++++++++++ examples/object/migration/observe-delete.yaml | 26 +++++++++++++++++++ examples/object/migration/observe.yaml | 26 +++++++++++++++++++ examples/object/object.yaml | 2 +- examples/object/policy/default.yaml | 2 +- .../object/policy/observe-create-update.yaml | 6 ++--- examples/object/policy/observe-delete.yaml | 6 ++--- examples/object/policy/observe.yaml | 6 ++--- .../object/references/depends-on-object.yaml | 4 +-- .../references/depends-on-resource.yaml | 2 +- .../patches-from-multiple-resources.yaml | 2 +- .../references/patches-from-resource.yaml | 2 +- 14 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 examples/object/migration/default.yaml create mode 100644 examples/object/migration/observe-create-update.yaml create mode 100644 examples/object/migration/observe-delete.yaml create mode 100644 examples/object/migration/observe.yaml diff --git a/examples/in-composition/composition.yaml b/examples/in-composition/composition.yaml index 40b802ee..68cde505 100644 --- a/examples/in-composition/composition.yaml +++ b/examples/in-composition/composition.yaml @@ -184,7 +184,7 @@ spec: string: fmt: "%s-argocd" - base: - apiVersion: kubernetes.crossplane.io/v1alpha1 + apiVersion: kubernetes.crossplane.io/v1beta1 kind: ProviderConfig spec: credentials: @@ -206,7 +206,7 @@ spec: readinessChecks: - type: None - base: - apiVersion: kubernetes.crossplane.io/v1alpha1 + apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object spec: forProvider: diff --git a/examples/object/migration/default.yaml b/examples/object/migration/default.yaml new file mode 100644 index 00000000..f19d7edb --- /dev/null +++ b/examples/object/migration/default.yaml @@ -0,0 +1,17 @@ +apiVersion: kubernetes.crossplane.io/v1alpha1 +kind: Object +metadata: + name: foo +spec: + # Use management policy Default to fully control k8s resource + # It is the default policy that can be omitted + forProvider: + manifest: + apiVersion: v1 + kind: ConfigMap + metadata: + # name in manifest is optional and defaults to Object name + # name: some-other-name + namespace: default + providerConfigRef: + name: kubernetes-provider diff --git a/examples/object/migration/observe-create-update.yaml b/examples/object/migration/observe-create-update.yaml new file mode 100644 index 00000000..87f83805 --- /dev/null +++ b/examples/object/migration/observe-create-update.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: kubernetes.crossplane.io/v1alpha1 +kind: Object +metadata: + name: foo +spec: + # Use management policy ObserveCreateUpdate to observe, create, or update k8s + # resource, but leave to third party to delete the resource + managementPolicy: ObserveCreateUpdate + forProvider: + manifest: + apiVersion: v1 + kind: ConfigMap + metadata: + # name in manifest is optional and defaults to Object name + # name: some-other-name + namespace: default + data: + sample-key: sample-value + providerConfigRef: + name: kubernetes-provider diff --git a/examples/object/migration/observe-delete.yaml b/examples/object/migration/observe-delete.yaml new file mode 100644 index 00000000..89cd1efe --- /dev/null +++ b/examples/object/migration/observe-delete.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + sample-key: sample-value +--- +apiVersion: kubernetes.crossplane.io/v1alpha1 +kind: Object +metadata: + name: foo +spec: + # Use management policy ObserveDelete to observe or delete k8s resource, + # but leave to third party to create or update the resource + managementPolicy: ObserveDelete + forProvider: + manifest: + apiVersion: v1 + kind: ConfigMap + metadata: + # name in manifest is optional and defaults to Object name + # name: some-other-name + namespace: default + providerConfigRef: + name: kubernetes-provider diff --git a/examples/object/migration/observe.yaml b/examples/object/migration/observe.yaml new file mode 100644 index 00000000..1df0c1d6 --- /dev/null +++ b/examples/object/migration/observe.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + sample-key: sample-value +--- +apiVersion: kubernetes.crossplane.io/v1alpha1 +kind: Object +metadata: + name: foo +spec: + # Use management policy Observe to observe k8s resource, + # but leave to third party to create, update, or delete the resource + managementPolicy: Observe + forProvider: + manifest: + apiVersion: v1 + kind: ConfigMap + metadata: + # name in manifest is optional and defaults to Object name + # name: some-other-name + namespace: default + providerConfigRef: + name: kubernetes-provider diff --git a/examples/object/object.yaml b/examples/object/object.yaml index 9c6f8674..aaf83f08 100644 --- a/examples/object/object.yaml +++ b/examples/object/object.yaml @@ -1,4 +1,4 @@ -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: sample-namespace diff --git a/examples/object/policy/default.yaml b/examples/object/policy/default.yaml index f19d7edb..d5748dde 100644 --- a/examples/object/policy/default.yaml +++ b/examples/object/policy/default.yaml @@ -1,4 +1,4 @@ -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo diff --git a/examples/object/policy/observe-create-update.yaml b/examples/object/policy/observe-create-update.yaml index 87f83805..4dcf8972 100644 --- a/examples/object/policy/observe-create-update.yaml +++ b/examples/object/policy/observe-create-update.yaml @@ -1,12 +1,12 @@ --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo spec: - # Use management policy ObserveCreateUpdate to observe, create, or update k8s + # Use management policy Observe Create Update to observe, create, or update k8s # resource, but leave to third party to delete the resource - managementPolicy: ObserveCreateUpdate + managementPolicies: ["Observe", "Create", "Update"] forProvider: manifest: apiVersion: v1 diff --git a/examples/object/policy/observe-delete.yaml b/examples/object/policy/observe-delete.yaml index 89cd1efe..bff38526 100644 --- a/examples/object/policy/observe-delete.yaml +++ b/examples/object/policy/observe-delete.yaml @@ -6,14 +6,14 @@ metadata: data: sample-key: sample-value --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo spec: - # Use management policy ObserveDelete to observe or delete k8s resource, + # Use management policy Observe Delete to observe or delete k8s resource, # but leave to third party to create or update the resource - managementPolicy: ObserveDelete + managementPolicies: ["Observe", "Delete"] forProvider: manifest: apiVersion: v1 diff --git a/examples/object/policy/observe.yaml b/examples/object/policy/observe.yaml index 1df0c1d6..1f94ddd6 100644 --- a/examples/object/policy/observe.yaml +++ b/examples/object/policy/observe.yaml @@ -6,14 +6,14 @@ metadata: data: sample-key: sample-value --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo spec: - # Use management policy Observe to observe k8s resource, + # Use management policies Observe to observe k8s resource, # but leave to third party to create, update, or delete the resource - managementPolicy: Observe + managementPolicies: ["Observe"] forProvider: manifest: apiVersion: v1 diff --git a/examples/object/references/depends-on-object.yaml b/examples/object/references/depends-on-object.yaml index 5c70651c..bee6d4f4 100644 --- a/examples/object/references/depends-on-object.yaml +++ b/examples/object/references/depends-on-object.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo @@ -22,7 +22,7 @@ spec: providerConfigRef: name: kubernetes-provider --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: bar diff --git a/examples/object/references/depends-on-resource.yaml b/examples/object/references/depends-on-resource.yaml index 84673fe9..33433724 100644 --- a/examples/object/references/depends-on-resource.yaml +++ b/examples/object/references/depends-on-resource.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo diff --git a/examples/object/references/patches-from-multiple-resources.yaml b/examples/object/references/patches-from-multiple-resources.yaml index e7490429..1a1e227c 100644 --- a/examples/object/references/patches-from-multiple-resources.yaml +++ b/examples/object/references/patches-from-multiple-resources.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo diff --git a/examples/object/references/patches-from-resource.yaml b/examples/object/references/patches-from-resource.yaml index c2821c6c..f6427fbe 100644 --- a/examples/object/references/patches-from-resource.yaml +++ b/examples/object/references/patches-from-resource.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1alpha1 +apiVersion: kubernetes.crossplane.io/v1beta1 kind: Object metadata: name: foo From 6eb22cb74d6fdfb18d8871c239139140971f3842 Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 4 Dec 2023 15:14:40 +0100 Subject: [PATCH 18/26] Deprecate v1alpha1 Signed-off-by: lsviben --- apis/object/v1alpha1/types.go | 2 ++ package/crds/kubernetes.crossplane.io_objects.yaml | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apis/object/v1alpha1/types.go b/apis/object/v1alpha1/types.go index 44ede7cb..bdbb103f 100644 --- a/apis/object/v1alpha1/types.go +++ b/apis/object/v1alpha1/types.go @@ -178,6 +178,8 @@ type ObjectStatus struct { // +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 v1beta1.Object type Object struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index ef8a086f..fe750ae4 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -54,10 +54,12 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date + deprecated: true name: v1alpha1 schema: openAPIV3Schema: - description: A Object is an provider Kubernetes API type + description: 'A Object is an provider Kubernetes API type Deprecated: v1alpha1.Object + is deprecated in favor of v1beta1.Object' properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation From cc27979b4d89954b1227ded36de4a40d57dfef03 Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 11 Dec 2023 12:53:07 +0100 Subject: [PATCH 19/26] review improvements Signed-off-by: lsviben --- Makefile | 5 +++++ cmd/provider/main.go | 4 ++++ examples/object/{migration => deprecated}/default.yaml | 0 .../{migration => deprecated}/observe-create-update.yaml | 0 .../object/{migration => deprecated}/observe-delete.yaml | 0 examples/object/{migration => deprecated}/observe.yaml | 0 6 files changed, 9 insertions(+) rename examples/object/{migration => deprecated}/default.yaml (100%) rename examples/object/{migration => deprecated}/observe-create-update.yaml (100%) rename examples/object/{migration => deprecated}/observe-delete.yaml (100%) rename examples/object/{migration => deprecated}/observe.yaml (100%) diff --git a/Makefile b/Makefile index 70b2bf3a..220a57e9 100644 --- a/Makefile +++ b/Makefile @@ -130,6 +130,11 @@ generate.run: kustomize.gen generate.done: kustomize.clean +# This hack is needed because we want to inject the conversion webhook +# configuration into the Object CRD. This is not possible with the CRD +# generation through controller-gen, and actually kubebuilder does +# something similar, so we are following suit. Can be removed once we +# drop support for v1alpha1. kustomize.gen: $(KUBECTL) @$(INFO) Generating CRDs with kustomize @$(KUBECTL) kustomize cluster/kustomize/ > cluster/kustomize/kubernetes.crossplane.io_objects.yaml diff --git a/cmd/provider/main.go b/cmd/provider/main.go index a071fb57..fe8298ce 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -105,6 +105,10 @@ func main() { log.Info("Beta feature enabled", "flag", feature.EnableBetaManagementPolicies) } + // NOTE(lsviben): We are registering the conversion webhook with v1alpha1 + // Object. As far as I can see and based on some tests, it doesn't matter + // which version we use here. Leaving it as v1alpha1 as it will be easy to + // notice and remove when we drop support for v1alpha1. kingpin.FatalIfError(ctrl.NewWebhookManagedBy(mgr).For(&v1alpha1.Object{}).Complete(), "Cannot create Object webhook") kingpin.FatalIfError(object.Setup(mgr, o), "Cannot setup controller") diff --git a/examples/object/migration/default.yaml b/examples/object/deprecated/default.yaml similarity index 100% rename from examples/object/migration/default.yaml rename to examples/object/deprecated/default.yaml diff --git a/examples/object/migration/observe-create-update.yaml b/examples/object/deprecated/observe-create-update.yaml similarity index 100% rename from examples/object/migration/observe-create-update.yaml rename to examples/object/deprecated/observe-create-update.yaml diff --git a/examples/object/migration/observe-delete.yaml b/examples/object/deprecated/observe-delete.yaml similarity index 100% rename from examples/object/migration/observe-delete.yaml rename to examples/object/deprecated/observe-delete.yaml diff --git a/examples/object/migration/observe.yaml b/examples/object/deprecated/observe.yaml similarity index 100% rename from examples/object/migration/observe.yaml rename to examples/object/deprecated/observe.yaml From ff8363b4f0e498ad0e9906f71532bcddc5d90a08 Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 11 Dec 2023 13:13:52 +0100 Subject: [PATCH 20/26] make sure go.generate is run before kustomize.gen Signed-off-by: lsviben --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 220a57e9..5534faec 100644 --- a/Makefile +++ b/Makefile @@ -126,7 +126,7 @@ manifests: .PHONY: cobertura submodules fallthrough test-integration run manifests -generate.run: kustomize.gen +generate.run: go.generate kustomize.gen generate.done: kustomize.clean From 7ca28d92cad42fd64a429a181c9be85134d9fc30 Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 11 Dec 2023 13:35:49 +0100 Subject: [PATCH 21/26] fix v1alpha1 leftovers after rebase Signed-off-by: lsviben --- internal/controller/object/object_test.go | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/controller/object/object_test.go b/internal/controller/object/object_test.go index d0c9c3cd..c6cce1bc 100644 --- a/internal/controller/object/object_test.go +++ b/internal/controller/object/object_test.go @@ -1895,10 +1895,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueWithoutConditions": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyAllTrue, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyAllTrue, }, }, }, @@ -1920,10 +1920,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueAndCantParse": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyAllTrue, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyAllTrue, }, }, }, @@ -1945,10 +1945,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueAndAnyConditionFalse": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyAllTrue, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyAllTrue, }, }, }, @@ -1981,10 +1981,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "AvailableIfAllTrueAndAllConditionsTrue": { args: args{ - obj: &v1alpha1.Object{ - Spec: v1alpha1.ObjectSpec{ - Readiness: v1alpha1.Readiness{ - Policy: v1alpha1.ReadinessPolicyAllTrue, + obj: &v1beta1.Object{ + Spec: v1beta1.ObjectSpec{ + Readiness: v1beta1.Readiness{ + Policy: v1beta1.ReadinessPolicyAllTrue, }, }, }, From ca2246de2b20164680e701d97479d55f8d4b9c69 Mon Sep 17 00:00:00 2001 From: lsviben Date: Thu, 14 Dec 2023 15:00:39 +0100 Subject: [PATCH 22/26] handle nil cases in conversion Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 32 ++-- apis/object/v1alpha1/conversion_test.go | 223 +++++++++++++++++++++++- 2 files changed, 235 insertions(+), 20 deletions(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index fe2efebe..7daf312b 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -50,14 +50,17 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // references := []v1beta1.Reference{} for _, r := range src.Spec.References { - references = append(references, v1beta1.Reference{ - DependsOn: &v1beta1.DependsOn{ + ref := v1beta1.Reference{} + if r.DependsOn != nil { + ref.DependsOn = &v1beta1.DependsOn{ APIVersion: r.DependsOn.APIVersion, Kind: r.DependsOn.Kind, Name: r.DependsOn.Name, Namespace: r.DependsOn.Namespace, - }, - PatchesFrom: &v1beta1.PatchesFrom{ + } + } + if r.PatchesFrom != nil { + ref.PatchesFrom = &v1beta1.PatchesFrom{ DependsOn: v1beta1.DependsOn{ APIVersion: r.PatchesFrom.APIVersion, Kind: r.PatchesFrom.Kind, @@ -65,8 +68,9 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // Namespace: r.PatchesFrom.Namespace, }, FieldPath: r.PatchesFrom.FieldPath, - }, - }) + } + } + references = append(references, ref) } dst.Spec = v1beta1.ObjectSpec{ @@ -125,14 +129,17 @@ func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint, references := []Reference{} for _, r := range src.Spec.References { - references = append(references, Reference{ - DependsOn: &DependsOn{ + ref := Reference{} + if r.DependsOn != nil { + ref.DependsOn = &DependsOn{ APIVersion: r.DependsOn.APIVersion, Kind: r.DependsOn.Kind, Name: r.DependsOn.Name, Namespace: r.DependsOn.Namespace, - }, - PatchesFrom: &PatchesFrom{ + } + } + if r.PatchesFrom != nil { + ref.PatchesFrom = &PatchesFrom{ DependsOn: DependsOn{ APIVersion: r.PatchesFrom.APIVersion, Kind: r.PatchesFrom.Kind, @@ -140,8 +147,9 @@ func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint, Namespace: r.PatchesFrom.Namespace, }, FieldPath: r.PatchesFrom.FieldPath, - }, - }) + } + } + references = append(references, ref) } dst.Spec = ObjectSpec{ diff --git a/apis/object/v1alpha1/conversion_test.go b/apis/object/v1alpha1/conversion_test.go index 21c7090f..47cabed9 100644 --- a/apis/object/v1alpha1/conversion_test.go +++ b/apis/object/v1alpha1/conversion_test.go @@ -24,6 +24,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/pointer" v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/test" @@ -70,8 +71,106 @@ func TestConvertTo(t *testing.T) { Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, ManagementPolicy: v1alpha1.Observe, - References: nil, - Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + References: []v1alpha1.Reference{ + { + DependsOn: &v1alpha1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + PatchesFrom: &v1alpha1.PatchesFrom{ + DependsOn: v1alpha1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + FieldPath: pointer.String("data.password"), + }, + }, + }, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + want: want{ + dst: &v1beta1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1beta1.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, + }, + ConnectionDetails: []v1beta1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1beta1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + References: []v1beta1.Reference{ + { + DependsOn: &v1beta1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + PatchesFrom: &v1beta1.PatchesFrom{ + DependsOn: v1beta1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + FieldPath: pointer.String("data.password"), + }, + }, + }, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + }, + { + name: "converts to v1beta1 - nil checks", + args: args{ + src: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ResourceSpec: v1alpha1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + }, + ConnectionDetails: []v1alpha1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1alpha1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ManagementPolicy: v1alpha1.Observe, + References: []v1alpha1.Reference{ + { + DependsOn: nil, + PatchesFrom: nil, + }, + }, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -97,8 +196,13 @@ func TestConvertTo(t *testing.T) { ForProvider: v1beta1.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{}, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + References: []v1beta1.Reference{ + { + DependsOn: nil, + PatchesFrom: nil, + }, + }, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -175,8 +279,106 @@ func TestConvertFrom(t *testing.T) { ForProvider: v1beta1.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{}, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + References: []v1beta1.Reference{ + { + DependsOn: &v1beta1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + PatchesFrom: &v1beta1.PatchesFrom{ + DependsOn: v1beta1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + FieldPath: pointer.String("data.password"), + }, + }, + }, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + want: want{ + dst: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ResourceSpec: v1alpha1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + }, + ConnectionDetails: []v1alpha1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1alpha1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ManagementPolicy: v1alpha1.Observe, + References: []v1alpha1.Reference{ + { + DependsOn: &v1alpha1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + PatchesFrom: &v1alpha1.PatchesFrom{ + DependsOn: v1alpha1.DependsOn{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + Namespace: "coolns", + }, + FieldPath: pointer.String("data.password"), + }, + }, + }, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + }, + }, + }, + }, + { + name: "converts to v1beta1 - nil checks", + args: args{ + src: &v1beta1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1beta1.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, + }, + ConnectionDetails: []v1beta1.ConnectionDetail{ + { + ObjectReference: corev1.ObjectReference{ + APIVersion: "v1", + Kind: "Secret", + Name: "topsecret", + }, + }, + }, + ForProvider: v1beta1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + References: []v1beta1.Reference{ + { + DependsOn: nil, + PatchesFrom: nil, + }, + }, + Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -202,8 +404,13 @@ func TestConvertFrom(t *testing.T) { Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, ManagementPolicy: v1alpha1.Observe, - References: []v1alpha1.Reference{}, - Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, + References: []v1alpha1.Reference{ + { + DependsOn: nil, + PatchesFrom: nil, + }, + }, + Readiness: v1alpha1.Readiness{Policy: v1alpha1.ReadinessPolicySuccessfulCreate}, }, }, }, From c8bbc9e5dcf8dc49d5911ad6c7ea7cfca460907e Mon Sep 17 00:00:00 2001 From: lsviben Date: Fri, 15 Dec 2023 13:09:32 +0100 Subject: [PATCH 23/26] get cert dir from env Signed-off-by: lsviben --- cmd/provider/main.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/cmd/provider/main.go b/cmd/provider/main.go index fe8298ce..7d077d2f 100644 --- a/cmd/provider/main.go +++ b/cmd/provider/main.go @@ -42,6 +42,12 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth" ) +const ( + webhookTLSCertDirEnvVar = "WEBHOOK_TLS_CERT_DIR" + tlsServerCertDirEnvVar = "TLS_SERVER_CERTS_DIR" + tlsServerCertDir = "/tls/server" +) + func main() { var ( app = kingpin.New(filepath.Base(os.Args[0]), "Template support for Crossplane.").DefaultEnvars() @@ -68,6 +74,18 @@ func main() { cfg, err := ctrl.GetConfig() kingpin.FatalIfError(err, "Cannot get API server rest config") + // Get the TLS certs directory from the environment variable if set + // In older XP versions we used WEBHOOK_TLS_CERT_DIR, in newer versions + // we use TLS_SERVER_CERTS_DIR. If neither are set, use the default. + var certDir string + certDir = os.Getenv(webhookTLSCertDirEnvVar) + if certDir == "" { + certDir = os.Getenv(tlsServerCertDirEnvVar) + if certDir == "" { + certDir = tlsServerCertDir + } + } + mgr, err := ctrl.NewManager(ratelimiter.LimitRESTConfig(cfg, *maxReconcileRate), ctrl.Options{ Cache: cache.Options{ SyncPeriod: syncInterval, @@ -86,7 +104,7 @@ func main() { LeaseDuration: func() *time.Duration { d := 60 * time.Second; return &d }(), RenewDeadline: func() *time.Duration { d := 50 * time.Second; return &d }(), WebhookServer: webhook.NewServer(webhook.Options{ - CertDir: filepath.Join("/", "tls", "server"), + CertDir: certDir, }), }) kingpin.FatalIfError(err, "Cannot create controller manager") From b825147f2f024caf2674385affacffcb162f3c18 Mon Sep 17 00:00:00 2001 From: lsviben Date: Mon, 8 Jan 2024 16:02:45 +0100 Subject: [PATCH 24/26] update to v1alpha2 instead of v1beta1 Signed-off-by: lsviben --- apis/kubernetes.go | 4 +- apis/object/v1alpha1/conversion.go | 36 ++-- apis/object/v1alpha1/conversion_test.go | 80 ++++---- apis/object/v1alpha1/types.go | 2 +- .../{v1beta1 => v1alpha2}/conversion.go | 2 +- apis/object/{v1beta1 => v1alpha2}/doc.go | 6 +- apis/object/{v1beta1 => v1alpha2}/register.go | 4 +- apis/object/{v1beta1 => v1alpha2}/types.go | 2 +- .../zz_generated.deepcopy.go | 2 +- .../zz_generated.managed.go | 2 +- .../zz_generated.managedlist.go | 2 +- cluster/kustomize/webhook/webhook.patch.yaml | 3 +- examples/object/object.yaml | 2 +- examples/object/policy/default.yaml | 2 +- .../object/policy/observe-create-update.yaml | 2 +- examples/object/policy/observe-delete.yaml | 2 +- examples/object/policy/observe.yaml | 2 +- .../object/references/depends-on-object.yaml | 4 +- .../references/depends-on-resource.yaml | 2 +- .../patches-from-multiple-resources.yaml | 2 +- .../references/patches-from-resource.yaml | 2 +- internal/controller/object/object.go | 53 ++--- internal/controller/object/object_test.go | 182 +++++++++--------- .../kubernetes.crossplane.io_objects.yaml | 7 +- 24 files changed, 203 insertions(+), 204 deletions(-) rename apis/object/{v1beta1 => v1alpha2}/conversion.go (97%) rename apis/object/{v1beta1 => v1alpha2}/doc.go (82%) rename apis/object/{v1beta1 => v1alpha2}/register.go (97%) rename apis/object/{v1beta1 => v1alpha2}/types.go (99%) rename apis/object/{v1beta1 => v1alpha2}/zz_generated.deepcopy.go (99%) rename apis/object/{v1beta1 => v1alpha2}/zz_generated.managed.go (99%) rename apis/object/{v1beta1 => v1alpha2}/zz_generated.managedlist.go (98%) diff --git a/apis/kubernetes.go b/apis/kubernetes.go index cbc0369e..f49b7515 100644 --- a/apis/kubernetes.go +++ b/apis/kubernetes.go @@ -21,7 +21,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" objectv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" - objectv1beta1 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + objectv1alhpa2 "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" templatev1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -30,7 +30,7 @@ func init() { AddToSchemes = append(AddToSchemes, templatev1alpha1.SchemeBuilder.AddToScheme, objectv1alpha1.SchemeBuilder.AddToScheme, - objectv1beta1.SchemeBuilder.AddToScheme, + objectv1alhpa2.SchemeBuilder.AddToScheme, ) } diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index 7daf312b..b79dcf12 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -24,35 +24,35 @@ import ( xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" ) -// ConvertTo converts this Object to the Hub version (v1beta1). +// ConvertTo converts this Object to the Hub version (v1alpha2). func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // We want to use different names for receiver parameter to be more clear. - dst := dstRaw.(*v1beta1.Object) + dst := dstRaw.(*v1alpha2.Object) // copy identical fields dst.ObjectMeta = src.ObjectMeta - dst.Status = v1beta1.ObjectStatus{ + dst.Status = v1alpha2.ObjectStatus{ ResourceStatus: src.Status.ResourceStatus, - AtProvider: v1beta1.ObjectObservation{ + AtProvider: v1alpha2.ObjectObservation{ Manifest: src.Status.AtProvider.Manifest, }, } - connectionDetails := []v1beta1.ConnectionDetail{} + connectionDetails := []v1alpha2.ConnectionDetail{} for _, cd := range src.Spec.ConnectionDetails { - connectionDetails = append(connectionDetails, v1beta1.ConnectionDetail{ + connectionDetails = append(connectionDetails, v1alpha2.ConnectionDetail{ ObjectReference: cd.ObjectReference, }) } - references := []v1beta1.Reference{} + references := []v1alpha2.Reference{} for _, r := range src.Spec.References { - ref := v1beta1.Reference{} + ref := v1alpha2.Reference{} if r.DependsOn != nil { - ref.DependsOn = &v1beta1.DependsOn{ + ref.DependsOn = &v1alpha2.DependsOn{ APIVersion: r.DependsOn.APIVersion, Kind: r.DependsOn.Kind, Name: r.DependsOn.Name, @@ -60,8 +60,8 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // } } if r.PatchesFrom != nil { - ref.PatchesFrom = &v1beta1.PatchesFrom{ - DependsOn: v1beta1.DependsOn{ + ref.PatchesFrom = &v1alpha2.PatchesFrom{ + DependsOn: v1alpha2.DependsOn{ APIVersion: r.PatchesFrom.APIVersion, Kind: r.PatchesFrom.Kind, Name: r.PatchesFrom.Name, @@ -73,7 +73,7 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // references = append(references, ref) } - dst.Spec = v1beta1.ObjectSpec{ + dst.Spec = v1alpha2.ObjectSpec{ ResourceSpec: xpv1.ResourceSpec{ WriteConnectionSecretToReference: src.GetWriteConnectionSecretToReference(), PublishConnectionDetailsTo: src.GetPublishConnectionDetailsTo(), @@ -81,12 +81,12 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // DeletionPolicy: src.GetDeletionPolicy(), }, ConnectionDetails: connectionDetails, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: src.Spec.ForProvider.Manifest, }, References: references, - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicy(src.Spec.Readiness.Policy), + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicy(src.Spec.Readiness.Policy), }, } @@ -107,9 +107,9 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // return nil } -// ConvertFrom converts from the Hub version (v1beta1) to this version. +// ConvertFrom converts from the Hub version (v1alpha2) to this version. func (dst *Object) ConvertFrom(srcRaw conversion.Hub) error { // nolint:golint, gocyclo // We want to use different names for receiver parameter to be more clear. - src := srcRaw.(*v1beta1.Object) + src := srcRaw.(*v1alpha2.Object) // copy identical fields dst.ObjectMeta = src.ObjectMeta diff --git a/apis/object/v1alpha1/conversion_test.go b/apis/object/v1alpha1/conversion_test.go index 47cabed9..91adc3e9 100644 --- a/apis/object/v1alpha1/conversion_test.go +++ b/apis/object/v1alpha1/conversion_test.go @@ -30,7 +30,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/test" "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha1" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" ) func TestConvertTo(t *testing.T) { @@ -39,7 +39,7 @@ func TestConvertTo(t *testing.T) { } type want struct { err error - dst *v1beta1.Object + dst *v1alpha2.Object } tests := []struct { @@ -48,7 +48,7 @@ func TestConvertTo(t *testing.T) { want want }{ { - name: "converts to v1beta1", + name: "converts to v1alpha2", args: args{ src: &v1alpha1.Object{ ObjectMeta: metav1.ObjectMeta{ @@ -95,16 +95,16 @@ func TestConvertTo(t *testing.T) { }, }, want: want{ - dst: &v1beta1.Object{ + dst: &v1alpha2.Object{ ObjectMeta: metav1.ObjectMeta{ Name: "coolobject", }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: v1.ResourceSpec{ DeletionPolicy: v1.DeletionDelete, ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, }, - ConnectionDetails: []v1beta1.ConnectionDetail{ + ConnectionDetails: []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ APIVersion: "v1", @@ -113,19 +113,19 @@ func TestConvertTo(t *testing.T) { }, }, }, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{ + References: []v1alpha2.Reference{ { - DependsOn: &v1beta1.DependsOn{ + DependsOn: &v1alpha2.DependsOn{ APIVersion: "v1", Kind: "Secret", Name: "topsecret", Namespace: "coolns", }, - PatchesFrom: &v1beta1.PatchesFrom{ - DependsOn: v1beta1.DependsOn{ + PatchesFrom: &v1alpha2.PatchesFrom{ + DependsOn: v1alpha2.DependsOn{ APIVersion: "v1", Kind: "Secret", Name: "topsecret", @@ -135,13 +135,13 @@ func TestConvertTo(t *testing.T) { }, }, }, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + Readiness: v1alpha2.Readiness{Policy: v1alpha2.ReadinessPolicySuccessfulCreate}, }, }, }, }, { - name: "converts to v1beta1 - nil checks", + name: "converts to v1alpha2 - nil checks", args: args{ src: &v1alpha1.Object{ ObjectMeta: metav1.ObjectMeta{ @@ -175,16 +175,16 @@ func TestConvertTo(t *testing.T) { }, }, want: want{ - dst: &v1beta1.Object{ + dst: &v1alpha2.Object{ ObjectMeta: metav1.ObjectMeta{ Name: "coolobject", }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: v1.ResourceSpec{ DeletionPolicy: v1.DeletionDelete, ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, }, - ConnectionDetails: []v1beta1.ConnectionDetail{ + ConnectionDetails: []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ APIVersion: "v1", @@ -193,16 +193,16 @@ func TestConvertTo(t *testing.T) { }, }, }, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{ + References: []v1alpha2.Reference{ { DependsOn: nil, PatchesFrom: nil, }, }, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + Readiness: v1alpha2.Readiness{Policy: v1alpha2.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -226,7 +226,7 @@ func TestConvertTo(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - beta := &v1beta1.Object{} + beta := &v1alpha2.Object{} err := tc.args.src.ConvertTo(beta) if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { t.Errorf("\nr.ConvertTo(...): -want error, +got error:\n%s", diff) @@ -243,7 +243,7 @@ func TestConvertTo(t *testing.T) { func TestConvertFrom(t *testing.T) { type args struct { - src *v1beta1.Object + src *v1alpha2.Object } type want struct { err error @@ -256,18 +256,18 @@ func TestConvertFrom(t *testing.T) { want want }{ { - name: "converts to v1beta1", + name: "converts to v1alpha2", args: args{ - src: &v1beta1.Object{ + src: &v1alpha2.Object{ ObjectMeta: metav1.ObjectMeta{ Name: "coolobject", }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: v1.ResourceSpec{ DeletionPolicy: v1.DeletionDelete, ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, }, - ConnectionDetails: []v1beta1.ConnectionDetail{ + ConnectionDetails: []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ APIVersion: "v1", @@ -276,19 +276,19 @@ func TestConvertFrom(t *testing.T) { }, }, }, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{ + References: []v1alpha2.Reference{ { - DependsOn: &v1beta1.DependsOn{ + DependsOn: &v1alpha2.DependsOn{ APIVersion: "v1", Kind: "Secret", Name: "topsecret", Namespace: "coolns", }, - PatchesFrom: &v1beta1.PatchesFrom{ - DependsOn: v1beta1.DependsOn{ + PatchesFrom: &v1alpha2.PatchesFrom{ + DependsOn: v1alpha2.DependsOn{ APIVersion: "v1", Kind: "Secret", Name: "topsecret", @@ -298,7 +298,7 @@ func TestConvertFrom(t *testing.T) { }, }, }, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + Readiness: v1alpha2.Readiness{Policy: v1alpha2.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -349,18 +349,18 @@ func TestConvertFrom(t *testing.T) { }, }, { - name: "converts to v1beta1 - nil checks", + name: "converts to v1alpha2 - nil checks", args: args{ - src: &v1beta1.Object{ + src: &v1alpha2.Object{ ObjectMeta: metav1.ObjectMeta{ Name: "coolobject", }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: v1.ResourceSpec{ DeletionPolicy: v1.DeletionDelete, ManagementPolicies: []v1.ManagementAction{v1.ManagementActionObserve}, }, - ConnectionDetails: []v1beta1.ConnectionDetail{ + ConnectionDetails: []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ APIVersion: "v1", @@ -369,16 +369,16 @@ func TestConvertFrom(t *testing.T) { }, }, }, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, }, - References: []v1beta1.Reference{ + References: []v1alpha2.Reference{ { DependsOn: nil, PatchesFrom: nil, }, }, - Readiness: v1beta1.Readiness{Policy: v1beta1.ReadinessPolicySuccessfulCreate}, + Readiness: v1alpha2.Readiness{Policy: v1alpha2.ReadinessPolicySuccessfulCreate}, }, }, }, @@ -418,11 +418,11 @@ func TestConvertFrom(t *testing.T) { { name: "errors if management policy is unknown", args: args{ - src: &v1beta1.Object{ + src: &v1alpha2.Object{ ObjectMeta: metav1.ObjectMeta{ Name: "coolobject", }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: v1.ResourceSpec{ ManagementPolicies: []v1.ManagementAction{}, }, diff --git a/apis/object/v1alpha1/types.go b/apis/object/v1alpha1/types.go index bdbb103f..d44eca82 100644 --- a/apis/object/v1alpha1/types.go +++ b/apis/object/v1alpha1/types.go @@ -179,7 +179,7 @@ type ObjectStatus struct { // +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 v1beta1.Object +// Deprecated: v1alpha1.Object is deprecated in favor of v1alpha2.Object type Object struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/apis/object/v1beta1/conversion.go b/apis/object/v1alpha2/conversion.go similarity index 97% rename from apis/object/v1beta1/conversion.go rename to apis/object/v1alpha2/conversion.go index d2ad6790..0f284409 100644 --- a/apis/object/v1beta1/conversion.go +++ b/apis/object/v1alpha2/conversion.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1alpha2 // Hub marks this type as a conversion hub. func (g *Object) Hub() { diff --git a/apis/object/v1beta1/doc.go b/apis/object/v1alpha2/doc.go similarity index 82% rename from apis/object/v1beta1/doc.go rename to apis/object/v1alpha2/doc.go index e55ae3c6..986868a9 100644 --- a/apis/object/v1beta1/doc.go +++ b/apis/object/v1alpha2/doc.go @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package v1beta1 contains the v1beta1 group Object resources of the Kubernetes provider. +// Package v1alpha2 contains the v1alpha2 group Object resources of the Kubernetes provider. // +kubebuilder:object:generate=true // +groupName=kubernetes.crossplane.io -// +versionName=v1beta1 -package v1beta1 +// +versionName=v1alpha2 +package v1alpha2 diff --git a/apis/object/v1beta1/register.go b/apis/object/v1alpha2/register.go similarity index 97% rename from apis/object/v1beta1/register.go rename to apis/object/v1alpha2/register.go index 87fc1f7e..c6b6ca22 100644 --- a/apis/object/v1beta1/register.go +++ b/apis/object/v1alpha2/register.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1alpha2 import ( "reflect" @@ -26,7 +26,7 @@ import ( // Package type metadata. const ( Group = "kubernetes.crossplane.io" - Version = "v1beta1" + Version = "v1alpha2" ) var ( diff --git a/apis/object/v1beta1/types.go b/apis/object/v1alpha2/types.go similarity index 99% rename from apis/object/v1beta1/types.go rename to apis/object/v1alpha2/types.go index b5e49161..5685fa14 100644 --- a/apis/object/v1beta1/types.go +++ b/apis/object/v1alpha2/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1beta1 +package v1alpha2 import ( v1 "k8s.io/api/core/v1" diff --git a/apis/object/v1beta1/zz_generated.deepcopy.go b/apis/object/v1alpha2/zz_generated.deepcopy.go similarity index 99% rename from apis/object/v1beta1/zz_generated.deepcopy.go rename to apis/object/v1alpha2/zz_generated.deepcopy.go index 35923c28..c0d78570 100644 --- a/apis/object/v1beta1/zz_generated.deepcopy.go +++ b/apis/object/v1alpha2/zz_generated.deepcopy.go @@ -18,7 +18,7 @@ limitations under the License. // Code generated by controller-gen. DO NOT EDIT. -package v1beta1 +package v1alpha2 import ( "k8s.io/apimachinery/pkg/runtime" diff --git a/apis/object/v1beta1/zz_generated.managed.go b/apis/object/v1alpha2/zz_generated.managed.go similarity index 99% rename from apis/object/v1beta1/zz_generated.managed.go rename to apis/object/v1alpha2/zz_generated.managed.go index f37ee992..9ed35007 100644 --- a/apis/object/v1beta1/zz_generated.managed.go +++ b/apis/object/v1alpha2/zz_generated.managed.go @@ -15,7 +15,7 @@ limitations under the License. */ // Code generated by angryjet. DO NOT EDIT. -package v1beta1 +package v1alpha2 import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" diff --git a/apis/object/v1beta1/zz_generated.managedlist.go b/apis/object/v1alpha2/zz_generated.managedlist.go similarity index 98% rename from apis/object/v1beta1/zz_generated.managedlist.go rename to apis/object/v1alpha2/zz_generated.managedlist.go index 56302560..b5000727 100644 --- a/apis/object/v1beta1/zz_generated.managedlist.go +++ b/apis/object/v1alpha2/zz_generated.managedlist.go @@ -15,7 +15,7 @@ limitations under the License. */ // Code generated by angryjet. DO NOT EDIT. -package v1beta1 +package v1alpha2 import resource "github.com/crossplane/crossplane-runtime/pkg/resource" diff --git a/cluster/kustomize/webhook/webhook.patch.yaml b/cluster/kustomize/webhook/webhook.patch.yaml index 74be3a6f..b7d321ec 100644 --- a/cluster/kustomize/webhook/webhook.patch.yaml +++ b/cluster/kustomize/webhook/webhook.patch.yaml @@ -7,8 +7,7 @@ spec: strategy: Webhook webhook: conversionReviewVersions: - - v1beta1 - - v1alpha1 + - v1 clientConfig: service: path: /convert \ No newline at end of file diff --git a/examples/object/object.yaml b/examples/object/object.yaml index aaf83f08..255e5b7b 100644 --- a/examples/object/object.yaml +++ b/examples/object/object.yaml @@ -1,4 +1,4 @@ -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: sample-namespace diff --git a/examples/object/policy/default.yaml b/examples/object/policy/default.yaml index d5748dde..936b9390 100644 --- a/examples/object/policy/default.yaml +++ b/examples/object/policy/default.yaml @@ -1,4 +1,4 @@ -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/policy/observe-create-update.yaml b/examples/object/policy/observe-create-update.yaml index 4dcf8972..16ec29f4 100644 --- a/examples/object/policy/observe-create-update.yaml +++ b/examples/object/policy/observe-create-update.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/policy/observe-delete.yaml b/examples/object/policy/observe-delete.yaml index bff38526..e2c23860 100644 --- a/examples/object/policy/observe-delete.yaml +++ b/examples/object/policy/observe-delete.yaml @@ -6,7 +6,7 @@ metadata: data: sample-key: sample-value --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/policy/observe.yaml b/examples/object/policy/observe.yaml index 1f94ddd6..43713523 100644 --- a/examples/object/policy/observe.yaml +++ b/examples/object/policy/observe.yaml @@ -6,7 +6,7 @@ metadata: data: sample-key: sample-value --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/references/depends-on-object.yaml b/examples/object/references/depends-on-object.yaml index bee6d4f4..09efa0f2 100644 --- a/examples/object/references/depends-on-object.yaml +++ b/examples/object/references/depends-on-object.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo @@ -22,7 +22,7 @@ spec: providerConfigRef: name: kubernetes-provider --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: bar diff --git a/examples/object/references/depends-on-resource.yaml b/examples/object/references/depends-on-resource.yaml index 33433724..62f5bfd3 100644 --- a/examples/object/references/depends-on-resource.yaml +++ b/examples/object/references/depends-on-resource.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/references/patches-from-multiple-resources.yaml b/examples/object/references/patches-from-multiple-resources.yaml index 1a1e227c..2d6b9e88 100644 --- a/examples/object/references/patches-from-multiple-resources.yaml +++ b/examples/object/references/patches-from-multiple-resources.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/examples/object/references/patches-from-resource.yaml b/examples/object/references/patches-from-resource.yaml index f6427fbe..843aaf3d 100644 --- a/examples/object/references/patches-from-resource.yaml +++ b/examples/object/references/patches-from-resource.yaml @@ -1,5 +1,5 @@ --- -apiVersion: kubernetes.crossplane.io/v1beta1 +apiVersion: kubernetes.crossplane.io/v1alpha2 kind: Object metadata: name: foo diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index c6282c14..750a42b3 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -44,7 +44,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" apisv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" "github.com/crossplane-contrib/provider-kubernetes/internal/clients" "github.com/crossplane-contrib/provider-kubernetes/internal/clients/azure" @@ -90,10 +90,10 @@ const ( // Setup adds a controller that reconciles Object managed resources. func Setup(mgr ctrl.Manager, o controller.Options) error { - name := managed.ControllerName(v1beta1.ObjectGroupKind) + name := managed.ControllerName(v1alpha2.ObjectGroupKind) cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} - + reconcilerOptions := []managed.ReconcilerOption{ managed.WithExternalConnecter(&connector{ logger: o.Logger, @@ -119,13 +119,14 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { } r := managed.NewReconciler(mgr, - resource.ManagedKind(v1beta1.ObjectGroupVersionKind), - reconcilerOptions...) + resource.ManagedKind(v1alpha2.ObjectGroupVersionKind), + reconcilerOptions..., + ) return ctrl.NewControllerManagedBy(mgr). Named(name). WithOptions(o.ForControllerRuntime()). - For(&v1beta1.Object{}). + For(&v1alpha2.Object{}). Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) } @@ -147,7 +148,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E // This method is currently a little over our complexity goal - be wary // of making it more complex. - cr, ok := mg.(*v1beta1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return nil, errors.New(errNotKubernetesObject) } @@ -242,7 +243,7 @@ type external struct { } func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { - cr, ok := mg.(*v1beta1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalObservation{}, errors.New(errNotKubernetesObject) } @@ -285,7 +286,7 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex } func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { - cr, ok := mg.(*v1beta1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalCreation{}, errors.New(errNotKubernetesObject) } @@ -309,7 +310,7 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { - cr, ok := mg.(*v1beta1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return managed.ExternalUpdate{}, errors.New(errNotKubernetesObject) } @@ -333,7 +334,7 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext } func (c *external) Delete(ctx context.Context, mg resource.Managed) error { - cr, ok := mg.(*v1beta1.Object) + cr, ok := mg.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -348,7 +349,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error { return errors.Wrap(resource.IgnoreNotFound(c.client.Delete(ctx, obj)), errDeleteObject) } -func getDesired(obj *v1beta1.Object) (*unstructured.Unstructured, error) { +func getDesired(obj *v1alpha2.Object) (*unstructured.Unstructured, error) { desired := &unstructured.Unstructured{} if err := json.Unmarshal(obj.Spec.ForProvider.Manifest.Raw, desired); err != nil { return nil, errors.Wrap(err, errUnmarshalTemplate) @@ -360,7 +361,7 @@ func getDesired(obj *v1beta1.Object) (*unstructured.Unstructured, error) { return desired, nil } -func getLastApplied(obj *v1beta1.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { +func getLastApplied(obj *v1alpha2.Object, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { lastApplied, ok := observed.GetAnnotations()[v1.LastAppliedConfigAnnotation] if !ok { return nil, nil @@ -378,7 +379,7 @@ func getLastApplied(obj *v1beta1.Object, observed *unstructured.Unstructured) (* return last, nil } -func (c *external) setObserved(obj *v1beta1.Object, observed *unstructured.Unstructured) error { +func (c *external) setObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { var err error if obj.Status.AtProvider.Manifest.Raw, err = observed.MarshalJSON(); err != nil { return errors.Wrap(err, errFailedToMarshalExisting) @@ -390,9 +391,9 @@ func (c *external) setObserved(obj *v1beta1.Object, observed *unstructured.Unstr return nil } -func (c *external) updateConditionFromObserved(obj *v1beta1.Object, observed *unstructured.Unstructured) error { +func (c *external) updateConditionFromObserved(obj *v1alpha2.Object, observed *unstructured.Unstructured) error { switch obj.Spec.Readiness.Policy { - case v1beta1.ReadinessPolicyDeriveFromObject: + case v1alpha2.ReadinessPolicyDeriveFromObject: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -406,7 +407,7 @@ func (c *external) updateConditionFromObserved(obj *v1beta1.Object, observed *un return nil } obj.SetConditions(xpv1.Available()) - case v1beta1.ReadinessPolicyAllTrue: + case v1alpha2.ReadinessPolicyAllTrue: conditioned := xpv1.ConditionedStatus{} err := fieldpath.Pave(observed.Object).GetValueInto("status", &conditioned) if err != nil { @@ -426,7 +427,7 @@ func (c *external) updateConditionFromObserved(obj *v1beta1.Object, observed *un } else { obj.SetConditions(xpv1.Unavailable()) } - case v1beta1.ReadinessPolicySuccessfulCreate, "": + case v1alpha2.ReadinessPolicySuccessfulCreate, "": // do nothing, will be handled by c.handleLastApplied method // "" should never happen, but just in case we will treat it as SuccessfulCreate for backward compatibility default: @@ -436,7 +437,7 @@ func (c *external) updateConditionFromObserved(obj *v1beta1.Object, observed *un return nil } -func getReferenceInfo(ref v1beta1.Reference) (string, string, string, string) { +func getReferenceInfo(ref v1alpha2.Reference) (string, string, string, string) { var apiVersion, kind, namespace, name string if ref.PatchesFrom != nil { @@ -459,7 +460,7 @@ func getReferenceInfo(ref v1beta1.Reference) (string, string, string, string) { // resolveReferencies resolves references for the current Object. If it fails to // resolve some reference, e.g.: due to reference not ready, it will then return // error and requeue to wait for resolving it next time. -func (c *external) resolveReferencies(ctx context.Context, obj *v1beta1.Object) error { +func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) error { c.logger.Debug("Resolving referencies.") // Loop through references to resolve each referenced resource @@ -493,7 +494,7 @@ func (c *external) resolveReferencies(ctx context.Context, obj *v1beta1.Object) return nil } -func (c *external) handleLastApplied(ctx context.Context, obj *v1beta1.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { +func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { isUpToDate := false if last != nil && equality.Semantic.DeepEqual(last, desired) { @@ -504,7 +505,7 @@ func (c *external) handleLastApplied(ctx context.Context, obj *v1beta1.Object, l if isUpToDate { c.logger.Debug("Up to date!") - if p := obj.Spec.Readiness.Policy; p == v1beta1.ReadinessPolicySuccessfulCreate || p == "" { + if p := obj.Spec.Readiness.Policy; p == v1alpha2.ReadinessPolicySuccessfulCreate || p == "" { obj.Status.SetConditions(xpv1.Available()) } @@ -533,7 +534,7 @@ type objFinalizer struct { type refFinalizerFn func(context.Context, *unstructured.Unstructured, string) error -func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1beta1.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { +func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1alpha2.Object, finalizerFn refFinalizerFn, ignoreNotFound bool) error { // Loop through references to resolve each referenced resource for _, ref := range obj.Spec.References { if ref.DependsOn == nil && ref.PatchesFrom == nil { @@ -569,7 +570,7 @@ func (f *objFinalizer) handleRefFinalizer(ctx context.Context, obj *v1beta1.Obje } func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1beta1.Object) + obj, ok := res.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -599,7 +600,7 @@ func (f *objFinalizer) AddFinalizer(ctx context.Context, res resource.Object) er } func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) error { - obj, ok := res.(*v1beta1.Object) + obj, ok := res.(*v1alpha2.Object) if !ok { return errors.New(errNotKubernetesObject) } @@ -628,7 +629,7 @@ func (f *objFinalizer) RemoveFinalizer(ctx context.Context, res resource.Object) return errors.Wrap(err, errRemoveFinalizer) } -func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1beta1.ConnectionDetail) (managed.ConnectionDetails, error) { +func connectionDetails(ctx context.Context, kube client.Client, connDetails []v1alpha2.ConnectionDetail) (managed.ConnectionDetails, error) { mcd := managed.ConnectionDetails{} for _, cd := range connDetails { diff --git a/internal/controller/object/object_test.go b/internal/controller/object/object_test.go index c6cce1bc..6010b49e 100644 --- a/internal/controller/object/object_test.go +++ b/internal/controller/object/object_test.go @@ -42,7 +42,7 @@ import ( "github.com/crossplane/crossplane-runtime/pkg/resource" "github.com/crossplane/crossplane-runtime/pkg/test" - "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1beta1" + "github.com/crossplane-contrib/provider-kubernetes/apis/object/v1alpha2" kubernetesv1alpha1 "github.com/crossplane-contrib/provider-kubernetes/apis/v1alpha1" ) @@ -80,30 +80,30 @@ type notKubernetesObject struct { resource.Managed } -type kubernetesObjectModifier func(obj *v1beta1.Object) +type kubernetesObjectModifier func(obj *v1alpha2.Object) type externalResourceModifier func(res *unstructured.Unstructured) -func kubernetesObject(om ...kubernetesObjectModifier) *v1beta1.Object { - o := &v1beta1.Object{ +func kubernetesObject(om ...kubernetesObjectModifier) *v1alpha2.Object { + o := &v1alpha2.Object{ TypeMeta: metav1.TypeMeta{ - APIVersion: v1beta1.SchemeGroupVersion.String(), - Kind: v1beta1.ObjectKind, + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.ObjectKind, }, ObjectMeta: metav1.ObjectMeta{ Name: testObjectName, Namespace: testNamespace, }, - Spec: v1beta1.ObjectSpec{ + Spec: v1alpha2.ObjectSpec{ ResourceSpec: xpv1.ResourceSpec{ ProviderConfigReference: &xpv1.Reference{ Name: providerName, }, }, - ForProvider: v1beta1.ObjectParameters{ + ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: externalResourceRaw}, }, }, - Status: v1beta1.ObjectStatus{}, + Status: v1alpha2.ObjectStatus{}, } for _, m := range om { @@ -145,16 +145,16 @@ func upToDateExternalResource() *unstructured.Unstructured { return externalResourceWithLastAppliedConfigAnnotation(string(externalResourceRaw)) } -func objectReferences() []v1beta1.Reference { - dependsOn := v1beta1.DependsOn{ - APIVersion: v1beta1.SchemeGroupVersion.String(), - Kind: v1beta1.ObjectKind, +func objectReferences() []v1alpha2.Reference { + dependsOn := v1alpha2.DependsOn{ + APIVersion: v1alpha2.SchemeGroupVersion.String(), + Kind: v1alpha2.ObjectKind, Name: testReferenceObjectName, Namespace: testNamespace, } - ref := []v1beta1.Reference{ + ref := []v1alpha2.Reference{ { - PatchesFrom: &v1beta1.PatchesFrom{ + PatchesFrom: &v1alpha2.PatchesFrom{ DependsOn: dependsOn, }, }, @@ -168,8 +168,8 @@ func objectReferences() []v1beta1.Reference { func referenceObject(rm ...externalResourceModifier) *unstructured.Unstructured { obj := &unstructured.Unstructured{ Object: map[string]interface{}{ - "apiVersion": v1beta1.SchemeGroupVersion.String(), - "kind": v1beta1.ObjectKind, + "apiVersion": v1alpha2.SchemeGroupVersion.String(), + "kind": v1alpha2.ObjectKind, "metadata": map[string]interface{}{ "name": testReferenceObjectName, "namespace": testNamespace, @@ -707,7 +707,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -767,7 +767,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "UpToDateNameDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -816,7 +816,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "FailedToPatchFieldFromReferenceObject": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() obj.Spec.References[0].PatchesFrom.FieldPath = pointer.String("nonexistent_field") }), @@ -837,7 +837,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -854,7 +854,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "NoExternalResourceExistsIfObjectWasDeleted": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.DeletionTimestamp = &metav1.Time{Time: time.Now()} obj.Spec.References = objectReferences() }), @@ -880,7 +880,7 @@ func Test_helmExternal_Observe(t *testing.T) { }, "ReferenceToObject": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -910,8 +910,8 @@ func Test_helmExternal_Observe(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { - obj.Spec.References = []v1beta1.Reference{{}} + mg: kubernetesObject(func(obj *v1alpha2.Object) { + obj.Spec.References = []v1alpha2.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -926,9 +926,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "ConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1beta1.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -972,9 +972,9 @@ func Test_helmExternal_Observe(t *testing.T) { }, "FailedToGetConnectionDetails": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() - obj.Spec.ConnectionDetails = []v1beta1.ConnectionDetail{ + obj.Spec.ConnectionDetails = []v1alpha2.ConnectionDetail{ { ObjectReference: corev1.ObjectReference{ Kind: "Secret", @@ -1048,7 +1048,7 @@ func Test_helmExternal_Create(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1071,7 +1071,7 @@ func Test_helmExternal_Create(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1156,7 +1156,7 @@ func Test_helmExternal_Update(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1179,7 +1179,7 @@ func Test_helmExternal_Update(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1251,7 +1251,7 @@ func Test_helmExternal_Delete(t *testing.T) { }, "NotAValidManifest": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{"test": "not-a-valid-manifest"}`) }), }, @@ -1274,7 +1274,7 @@ func Test_helmExternal_Delete(t *testing.T) { }, "SuccessDefaultsToObjectName": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.ForProvider.Manifest.Raw = []byte(`{ "apiVersion": "v1", "kind": "Namespace" }`) @@ -1357,7 +1357,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "ObjectFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), }, @@ -1367,7 +1367,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "NoReferenceObjectExists": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1385,8 +1385,8 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "EmptyReference": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { - obj.Spec.References = []v1beta1.Reference{{}} + mg: kubernetesObject(func(obj *v1alpha2.Object) { + obj.Spec.References = []v1alpha2.Reference{{}} }), client: resource.ClientApplicator{ Client: &test.MockClient{ @@ -1400,7 +1400,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "FailedToAddReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1427,7 +1427,7 @@ func Test_objFinalizer_AddFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.Spec.References = objectReferences() }), client: resource.ClientApplicator{ @@ -1479,7 +1479,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveObjectFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) }), client: resource.ClientApplicator{ @@ -1504,7 +1504,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "NoReferenceFinalizerExists": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() }), @@ -1525,7 +1525,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "ReferenceNotFound": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1544,7 +1544,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "FailedToRemoveReferenceFinalizer": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1574,7 +1574,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { }, "Success": { args: args{ - mg: kubernetesObject(func(obj *v1beta1.Object) { + mg: kubernetesObject(func(obj *v1alpha2.Object) { obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, objFinalizerName) obj.Spec.References = objectReferences() obj.ObjectMeta.UID = someUID @@ -1606,7 +1606,7 @@ func Test_objFinalizer_RemoveFinalizer(t *testing.T) { t.Errorf("f.RemoveFinalizer(...): -want error, +got error: %s", diff) } - if _, ok := tc.args.mg.(*v1beta1.Object); ok { + if _, ok := tc.args.mg.(*v1alpha2.Object); ok { sort := cmpopts.SortSlices(func(a, b string) bool { return a < b }) if diff := cmp.Diff(tc.want.finalizers, tc.args.mg.GetFinalizers(), sort); diff != "" { t.Errorf("managed resource finalizers: -want, +got: %s", diff) @@ -1632,7 +1632,7 @@ func Test_connectionDetails(t *testing.T) { } } - connDetail := v1beta1.ConnectionDetail{ + connDetail := v1alpha2.ConnectionDetail{ ObjectReference: corev1.ObjectReference{ Kind: "Secret", Namespace: testNamespace, @@ -1645,7 +1645,7 @@ func Test_connectionDetails(t *testing.T) { type args struct { kube client.Client - connDetails []v1beta1.ConnectionDetail + connDetails []v1alpha2.ConnectionDetail } type want struct { out managed.ConnectionDetails @@ -1661,7 +1661,7 @@ func Test_connectionDetails(t *testing.T) { map[string]interface{}{}, kerrors.NewNotFound(schema.GroupResource{Group: "", Resource: "secrets"}, testSecretName), ), - connDetails: []v1beta1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1676,7 +1676,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1beta1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1691,7 +1691,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1beta1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{}, @@ -1706,7 +1706,7 @@ func Test_connectionDetails(t *testing.T) { }, nil, ), - connDetails: []v1beta1.ConnectionDetail{connDetail}, + connDetails: []v1alpha2.ConnectionDetail{connDetail}, }, want: want{ out: managed.ConnectionDetails{ @@ -1730,7 +1730,7 @@ func Test_connectionDetails(t *testing.T) { func Test_updateConditionFromObserved(t *testing.T) { type args struct { - obj *v1beta1.Object + obj *v1alpha2.Object observed *unstructured.Unstructured } type want struct { @@ -1743,7 +1743,7 @@ func Test_updateConditionFromObserved(t *testing.T) { }{ "NoopIfNoPolicyDefined": { args: args{ - obj: &v1beta1.Object{}, + obj: &v1alpha2.Object{}, observed: &unstructured.Unstructured{ Object: map[string]interface{}{ "status": xpv1.ConditionedStatus{}, @@ -1757,10 +1757,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "NoopIfSuccessfulCreatePolicyDefined": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicySuccessfulCreate, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicySuccessfulCreate, }, }, }, @@ -1777,10 +1777,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDeriveFromObjectAndNotReady": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1810,10 +1810,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndNoCondition": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1836,10 +1836,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "AvailableIfDeriveFromObjectAndReady": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1869,10 +1869,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfDerivedFromObjectAndCantParse": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyDeriveFromObject, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyDeriveFromObject, }, }, }, @@ -1895,10 +1895,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueWithoutConditions": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyAllTrue, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyAllTrue, }, }, }, @@ -1920,10 +1920,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueAndCantParse": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyAllTrue, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyAllTrue, }, }, }, @@ -1945,10 +1945,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "UnavailableIfAllTrueAndAnyConditionFalse": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyAllTrue, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyAllTrue, }, }, }, @@ -1981,10 +1981,10 @@ func Test_updateConditionFromObserved(t *testing.T) { }, "AvailableIfAllTrueAndAllConditionsTrue": { args: args{ - obj: &v1beta1.Object{ - Spec: v1beta1.ObjectSpec{ - Readiness: v1beta1.Readiness{ - Policy: v1beta1.ReadinessPolicyAllTrue, + obj: &v1alpha2.Object{ + Spec: v1alpha2.ObjectSpec{ + Readiness: v1alpha2.Readiness{ + Policy: v1alpha2.ReadinessPolicyAllTrue, }, }, }, diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index fe750ae4..f097a70d 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -12,8 +12,7 @@ spec: service: path: /convert conversionReviewVersions: - - v1beta1 - - v1alpha1 + - v1 group: kubernetes.crossplane.io names: categories: @@ -59,7 +58,7 @@ spec: schema: openAPIV3Schema: description: 'A Object is an provider Kubernetes API type Deprecated: v1alpha1.Object - is deprecated in favor of v1beta1.Object' + is deprecated in favor of v1alpha2.Object' properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -486,7 +485,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: AGE type: date - name: v1beta1 + name: v1alpha2 schema: openAPIV3Schema: description: A Object is an provider Kubernetes API type From 1e57b702007c60addbf2bf149a3ffab0539b8117 Mon Sep 17 00:00:00 2001 From: lsviben Date: Tue, 9 Jan 2024 12:29:56 +0100 Subject: [PATCH 25/26] handle empty management policy Signed-off-by: lsviben --- apis/object/v1alpha1/conversion.go | 2 +- apis/object/v1alpha1/conversion_test.go | 37 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apis/object/v1alpha1/conversion.go b/apis/object/v1alpha1/conversion.go index b79dcf12..3c374ff1 100644 --- a/apis/object/v1alpha1/conversion.go +++ b/apis/object/v1alpha1/conversion.go @@ -92,7 +92,7 @@ func (src *Object) ConvertTo(dstRaw conversion.Hub) error { // nolint:golint // // handle management policies migration switch src.Spec.ManagementPolicy { - case Default: + case Default, "": dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionAll} case ObserveCreateUpdate: dst.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionObserve, xpv1.ManagementActionCreate, xpv1.ManagementActionUpdate} diff --git a/apis/object/v1alpha1/conversion_test.go b/apis/object/v1alpha1/conversion_test.go index 91adc3e9..bc68c3a3 100644 --- a/apis/object/v1alpha1/conversion_test.go +++ b/apis/object/v1alpha1/conversion_test.go @@ -140,6 +140,43 @@ func TestConvertTo(t *testing.T) { }, }, }, + { + name: "converts to v1alpha2 - empty policy", + args: args{ + src: &v1alpha1.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha1.ObjectSpec{ + ResourceSpec: v1alpha1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + }, + ForProvider: v1alpha1.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ManagementPolicy: "", + }, + }, + }, + want: want{ + dst: &v1alpha2.Object{ + ObjectMeta: metav1.ObjectMeta{ + Name: "coolobject", + }, + Spec: v1alpha2.ObjectSpec{ + ResourceSpec: v1.ResourceSpec{ + DeletionPolicy: v1.DeletionDelete, + ManagementPolicies: []v1.ManagementAction{v1.ManagementActionAll}, + }, + ForProvider: v1alpha2.ObjectParameters{ + Manifest: runtime.RawExtension{Raw: []byte("apiVersion: v1\nkind: Secret\nmetadata:\n name: topsecret\n")}, + }, + ConnectionDetails: []v1alpha2.ConnectionDetail{}, + References: []v1alpha2.Reference{}, + }, + }, + }, + }, { name: "converts to v1alpha2 - nil checks", args: args{ From e3215feb80ce1e6e007d5068620c128a29b4b332 Mon Sep 17 00:00:00 2001 From: lsviben Date: Wed, 10 Jan 2024 13:35:27 +0100 Subject: [PATCH 26/26] handle non controlling management policy readiness Signed-off-by: lsviben --- apis/object/v1alpha2/types.go | 2 +- internal/controller/object/object.go | 6 +++++ internal/controller/object/object_test.go | 23 +++++++++++++++++++ .../kubernetes.crossplane.io_objects.yaml | 1 + 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apis/object/v1alpha2/types.go b/apis/object/v1alpha2/types.go index 5685fa14..75210ed8 100644 --- a/apis/object/v1alpha2/types.go +++ b/apis/object/v1alpha2/types.go @@ -120,7 +120,7 @@ const ( type Readiness struct { // Policy defines how the Object's readiness condition should be computed. // +optional - // +kubebuilder:validation:Enum=SuccessfulCreate;DeriveFromObject + // +kubebuilder:validation:Enum=SuccessfulCreate;DeriveFromObject;AllTrue // +kubebuilder:default=SuccessfulCreate Policy ReadinessPolicy `json:"policy,omitempty"` } diff --git a/internal/controller/object/object.go b/internal/controller/object/object.go index 750a42b3..82f25a13 100644 --- a/internal/controller/object/object.go +++ b/internal/controller/object/object.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -497,6 +498,11 @@ func (c *external) resolveReferencies(ctx context.Context, obj *v1alpha2.Object) func (c *external) handleLastApplied(ctx context.Context, obj *v1alpha2.Object, last, desired *unstructured.Unstructured) (managed.ExternalObservation, error) { isUpToDate := false + if !sets.New[xpv1.ManagementAction](obj.GetManagementPolicies()...). + HasAny(xpv1.ManagementActionUpdate, xpv1.ManagementActionCreate, xpv1.ManagementActionAll) { + // Treated as up-to-date as we don't update or create the resource + isUpToDate = true + } if last != nil && equality.Semantic.DeepEqual(last, desired) { // Mark as up-to-date since last is equal to desired isUpToDate = true diff --git a/internal/controller/object/object_test.go b/internal/controller/object/object_test.go index 6010b49e..2373366a 100644 --- a/internal/controller/object/object_test.go +++ b/internal/controller/object/object_test.go @@ -98,6 +98,7 @@ func kubernetesObject(om ...kubernetesObjectModifier) *v1alpha2.Object { ProviderConfigReference: &xpv1.Reference{ Name: providerName, }, + ManagementPolicies: xpv1.ManagementPolicies{xpv1.ManagementActionAll}, }, ForProvider: v1alpha2.ObjectParameters{ Manifest: runtime.RawExtension{Raw: externalResourceRaw}, @@ -1005,6 +1006,28 @@ func Test_helmExternal_Observe(t *testing.T) { err: errors.Wrap(errors.Wrap(errBoom, errGetObject), errGetConnectionDetails), }, }, + "Observe Only - up to date by default": { + args: args{ + mg: kubernetesObject(func(obj *v1alpha2.Object) { + obj.Spec.ManagementPolicies = xpv1.ManagementPolicies{xpv1.ManagementActionObserve} + }), + client: resource.ClientApplicator{ + Client: &test.MockClient{ + MockGet: test.NewMockGetFn(nil, func(obj client.Object) error { + *obj.(*unstructured.Unstructured) = + *externalResourceWithLastAppliedConfigAnnotation( + `{"apiVersion":"v1","kind":"Namespace","metadata":{"name":"crossplane-system", "labels": {"old-label":"gone"}}}`, + ) + return nil + }), + }, + }, + }, + want: want{ + out: managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: true, ConnectionDetails: managed.ConnectionDetails{}}, + err: nil, + }, + }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { diff --git a/package/crds/kubernetes.crossplane.io_objects.yaml b/package/crds/kubernetes.crossplane.io_objects.yaml index f097a70d..9e8f2a5f 100644 --- a/package/crds/kubernetes.crossplane.io_objects.yaml +++ b/package/crds/kubernetes.crossplane.io_objects.yaml @@ -721,6 +721,7 @@ spec: enum: - SuccessfulCreate - DeriveFromObject + - AllTrue type: string type: object references: