diff --git a/apis/core/v1alpha1/bindingphase.go b/apis/core/v1alpha1/bindingphase.go deleted file mode 100644 index 482cf4f..0000000 --- a/apis/core/v1alpha1/bindingphase.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2019 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 - -// BindingPhase represents the current binding phase of a resource or claim. -type BindingPhase string - -// Binding phases. -const ( - // BindingPhaseUnset resources cannot be bound to another resource because - // they are in an unset binding phase, presumed to be functionally - // equivalent to BindingPhaseUnbindable. - BindingPhaseUnset BindingPhase = "" - - // BindingPhaseUnbindable resources cannot be bound to another resource, for - // example because they are currently unavailable, or being created. - BindingPhaseUnbindable BindingPhase = "Unbindable" - - // BindingPhaseUnbound resources are available for binding to another - // resource. - BindingPhaseUnbound BindingPhase = "Unbound" - - // BindingPhaseBound resources are bound to another resource. - BindingPhaseBound BindingPhase = "Bound" - - // BindingPhaseReleased managed resources were bound to a resource claim - // that has since been deleted. Released managed resources cannot be - // reclaimed; they are retained to allow manual clean-up and deletion. - BindingPhaseReleased BindingPhase = "Released" -) - -// A BindingStatus represents the bindability and binding status of a resource. -type BindingStatus struct { - // Phase represents the binding phase of a managed resource or claim. - // Unbindable resources cannot be bound, typically because they are - // currently unavailable, or still being created. Unbound resource are - // available for binding, and Bound resources have successfully bound to - // another resource. - // Deprecated. See https://github.com/crossplane/crossplane/issues/1670 - // +optional - // +kubebuilder:validation:Enum=Unbindable;Unbound;Bound;Released - Phase BindingPhase `json:"bindingPhase,omitempty"` -} - -// SetBindingPhase sets the binding phase of the resource. -func (s *BindingStatus) SetBindingPhase(p BindingPhase) { - s.Phase = p -} - -// GetBindingPhase gets the binding phase of the resource. -func (s *BindingStatus) GetBindingPhase() BindingPhase { - return s.Phase -} diff --git a/apis/core/v1alpha1/policies.go b/apis/core/v1alpha1/policies.go index a85a65a..23e9a60 100644 --- a/apis/core/v1alpha1/policies.go +++ b/apis/core/v1alpha1/policies.go @@ -16,21 +16,6 @@ limitations under the License. package v1alpha1 -// A ReclaimPolicy determines what should happen to managed resources when their -// bound resource claims are deleted. -type ReclaimPolicy string - -const ( - // ReclaimDelete means both the managed resource and its underlying external - // resource will be deleted when its bound resource claim is deleted. - ReclaimDelete ReclaimPolicy = "Delete" - - // ReclaimRetain means the managed resource will retained when its bound - // resource claim is deleted. Furthermore, its underlying external resource - // will be retained when the managed resource is deleted. - ReclaimRetain ReclaimPolicy = "Retain" -) - // A DeletionPolicy determines what should happen to the underlying external // resource when a managed resource is deleted. type DeletionPolicy string diff --git a/apis/core/v1alpha1/resource.go b/apis/core/v1alpha1/resource.go index 6378ae9..c161802 100644 --- a/apis/core/v1alpha1/resource.go +++ b/apis/core/v1alpha1/resource.go @@ -19,20 +19,10 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" - // NOTE(negz): Importing this as metav1 appears to break controller-gen's - // deepcopy generation logic. It generates a deepcopy file that omits this - // import and thus does not compile. Importing as v1 fixes this. ¯\_(ツ)_/¯ - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ) -// The annotation used to make a resource class the default. -const ( - AnnotationDefaultClassKey = "resourceclass.crossplane.io/is-default-class" - AnnotationDefaultClassValue = "true" -) - const ( // ResourceCredentialsSecretEndpointKey is the key inside a connection secret for the connection endpoint ResourceCredentialsSecretEndpointKey = "endpoint" @@ -130,47 +120,6 @@ func (obj *TypedReference) GroupVersionKind() schema.GroupVersionKind { // GetObjectKind get the ObjectKind of a TypedReference. func (obj *TypedReference) GetObjectKind() schema.ObjectKind { return obj } -// A ResourceClaimSpec defines the desired state of a resource claim. -// Deprecated. See https://github.com/crossplane/crossplane/issues/1670 -type ResourceClaimSpec struct { - // WriteConnectionSecretToReference specifies the name of a Secret, in the - // same namespace as this resource claim, to which any connection details - // for this resource claim should be written. Connection details frequently - // include the endpoint, username, and password required to connect to the - // managed resource bound to this resource claim. - // +optional - WriteConnectionSecretToReference *LocalSecretReference `json:"writeConnectionSecretToRef,omitempty"` - - // TODO(negz): Make the below references immutable once set? Doing so means - // we don't have to track what provisioner was used to create a resource. - - // A ClassSelector specifies labels that will be used to select a resource - // class for this claim. If multiple classes match the labels one will be - // chosen at random. - // +optional - ClassSelector *v1.LabelSelector `json:"classSelector,omitempty"` - - // A ClassReference specifies a resource class that will be used to - // dynamically provision a managed resource when the resource claim is - // created. - // +optional - ClassReference *corev1.ObjectReference `json:"classRef,omitempty"` - - // A ResourceReference specifies an existing managed resource, in any - // namespace, to which this resource claim should attempt to bind. Omit the - // resource reference to enable dynamic provisioning using a resource class; - // the resource reference will be automatically populated by Crossplane. - // +optional - ResourceReference *corev1.ObjectReference `json:"resourceRef,omitempty"` -} - -// A ResourceClaimStatus represents the observed status of a resource claim. -// Deprecated. See https://github.com/crossplane/crossplane/issues/1670 -type ResourceClaimStatus struct { - ConditionedStatus `json:",inline"` - BindingStatus `json:",inline"` -} - // TODO(negz): Rename Resource* to Managed* to clarify that they enable the // resource.Managed interface. @@ -183,20 +132,6 @@ type ResourceSpec struct { // +optional WriteConnectionSecretToReference *SecretReference `json:"writeConnectionSecretToRef,omitempty"` - // ClaimReference specifies the resource claim to which this managed - // resource will be bound. ClaimReference is set automatically during - // dynamic provisioning. - // Deprecated. See https://github.com/crossplane/crossplane/issues/1670 - // - // +optional - ClaimReference *corev1.ObjectReference `json:"claimRef,omitempty"` - - // ClassReference specifies the resource class that was used to dynamically - // provision this managed resource, if any. - // Deprecated. See https://github.com/crossplane/crossplane/issues/1670 - // +optional - ClassReference *corev1.ObjectReference `json:"classRef,omitempty"` - // ProviderConfigReference specifies how the provider that will be used to // create, observe, update, and delete this managed resource should be // configured. @@ -215,59 +150,11 @@ type ResourceSpec struct { // +optional // +kubebuilder:validation:Enum=Orphan;Delete DeletionPolicy DeletionPolicy `json:"deletionPolicy,omitempty"` - - // ReclaimPolicy specifies what will happen to this managed resource when - // its resource claim is deleted, and what will happen to the underlying - // external resource when the managed resource is deleted. The "Delete" - // policy causes the managed resource to be deleted when its bound resource - // claim is deleted, and in turn causes the external resource to be deleted - // when its managed resource is deleted. The "Retain" policy causes the - // managed resource to be retained, in binding phase "Released", when its - // resource claim is deleted, and in turn causes the external resource to be - // retained when its managed resource is deleted. The "Delete" policy is - // used when no policy is specified. - // - // Deprecated. DeletionPolicy takes precedence when both are set. - // See https://github.com/crossplane/crossplane-runtime/issues/179. - // - // +optional - // +kubebuilder:validation:Enum=Retain;Delete - ReclaimPolicy ReclaimPolicy `json:"reclaimPolicy,omitempty"` } // ResourceStatus represents the observed state of a managed resource. type ResourceStatus struct { ConditionedStatus `json:",inline"` - BindingStatus `json:",inline"` -} - -// A ClassSpecTemplate defines a template that will be used to create the -// specifications of managed resources dynamically provisioned using a resource -// class. -type ClassSpecTemplate struct { - // WriteConnectionSecretsToNamespace specifies the namespace in which the - // connection secrets of managed resources dynamically provisioned using - // this claim will be created. - WriteConnectionSecretsToNamespace string `json:"writeConnectionSecretsToNamespace"` - - // ProviderReference specifies the provider that will be used to create, - // observe, update, and delete managed resources that are dynamically - // provisioned using this resource class. - ProviderReference Reference `json:"providerRef"` - - // ReclaimPolicy specifies what will happen to managed resources dynamically - // provisioned using this class when their resource claims are deleted, and - // what will happen to their underlying external resource when they are - // deleted. The "Delete" policy causes the managed resource to be deleted - // when its bound resource claim is deleted, and in turn causes the external - // resource to be deleted when its managed resource is deleted. The "Retain" - // policy causes the managed resource to be retained, in binding phase - // "Released", when its resource claim is deleted, and in turn causes the - // external resource to be retained when its managed resource is deleted. - // The "Delete" policy is used when no policy is specified. - // +optional - // +kubebuilder:validation:Enum=Retain;Delete - ReclaimPolicy ReclaimPolicy `json:"reclaimPolicy,omitempty"` } // A ProviderSpec defines the common way to get to the necessary objects to connect diff --git a/apis/core/v1alpha1/zz_generated.deepcopy.go b/apis/core/v1alpha1/zz_generated.deepcopy.go index 3ffc2c3..40970e3 100644 --- a/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -21,41 +21,9 @@ limitations under the License. package v1alpha1 import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/api/core/v1" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BindingStatus) DeepCopyInto(out *BindingStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BindingStatus. -func (in *BindingStatus) DeepCopy() *BindingStatus { - if in == nil { - return nil - } - out := new(BindingStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClassSpecTemplate) DeepCopyInto(out *ClassSpecTemplate) { - *out = *in - out.ProviderReference = in.ProviderReference -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClassSpecTemplate. -func (in *ClassSpecTemplate) DeepCopy() *ClassSpecTemplate { - if in == nil { - return nil - } - out := new(ClassSpecTemplate) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in @@ -164,58 +132,6 @@ func (in *Reference) DeepCopy() *Reference { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResourceClaimSpec) DeepCopyInto(out *ResourceClaimSpec) { - *out = *in - if in.WriteConnectionSecretToReference != nil { - in, out := &in.WriteConnectionSecretToReference, &out.WriteConnectionSecretToReference - *out = new(LocalSecretReference) - **out = **in - } - if in.ClassSelector != nil { - in, out := &in.ClassSelector, &out.ClassSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.ClassReference != nil { - in, out := &in.ClassReference, &out.ClassReference - *out = new(corev1.ObjectReference) - **out = **in - } - if in.ResourceReference != nil { - in, out := &in.ResourceReference, &out.ResourceReference - *out = new(corev1.ObjectReference) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceClaimSpec. -func (in *ResourceClaimSpec) DeepCopy() *ResourceClaimSpec { - if in == nil { - return nil - } - out := new(ResourceClaimSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResourceClaimStatus) DeepCopyInto(out *ResourceClaimStatus) { - *out = *in - in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus) - out.BindingStatus = in.BindingStatus -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceClaimStatus. -func (in *ResourceClaimStatus) DeepCopy() *ResourceClaimStatus { - if in == nil { - return nil - } - out := new(ResourceClaimStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) { *out = *in @@ -224,16 +140,6 @@ func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) { *out = new(SecretReference) **out = **in } - if in.ClaimReference != nil { - in, out := &in.ClaimReference, &out.ClaimReference - *out = new(corev1.ObjectReference) - **out = **in - } - if in.ClassReference != nil { - in, out := &in.ClassReference, &out.ClassReference - *out = new(corev1.ObjectReference) - **out = **in - } if in.ProviderConfigReference != nil { in, out := &in.ProviderConfigReference, &out.ProviderConfigReference *out = new(Reference) @@ -260,7 +166,6 @@ func (in *ResourceSpec) DeepCopy() *ResourceSpec { func (in *ResourceStatus) DeepCopyInto(out *ResourceStatus) { *out = *in in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus) - out.BindingStatus = in.BindingStatus } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceStatus. @@ -341,7 +246,7 @@ func (in *TargetSpec) DeepCopyInto(out *TargetSpec) { } if in.ResourceReference != nil { in, out := &in.ResourceReference, &out.ResourceReference - *out = new(corev1.ObjectReference) + *out = new(v1.ObjectReference) **out = **in } } diff --git a/pkg/reconciler/claimbinding/api.go b/pkg/reconciler/claimbinding/api.go deleted file mode 100644 index f28be95..0000000 --- a/pkg/reconciler/claimbinding/api.go +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" -) - -// Error strings. -const ( - errCreateManaged = "cannot create managed resource" - errUpdateClaim = "cannot update resource claim" - errUpdateManaged = "cannot update managed resource" - errUpdateManagedStatus = "cannot update managed resource status" - errDeleteManaged = "cannot delete managed resource" - errBindMismatch = "refusing to bind to managed resource that does not reference resource claim" - errUnbindMismatch = "refusing to 'unbind' from managed resource that does not reference resource claim" - errBindControlled = "refusing to bind to managed resource that is controlled by another resource" -) - -// An APIManagedCreator creates resources by submitting them to a Kubernetes -// API server. -type APIManagedCreator struct { - client client.Client - typer runtime.ObjectTyper -} - -// NewAPIManagedCreator returns a new APIManagedCreator. -func NewAPIManagedCreator(c client.Client, t runtime.ObjectTyper) *APIManagedCreator { - return &APIManagedCreator{client: c, typer: t} -} - -// Create the supplied resource using the supplied class and claim. -func (a *APIManagedCreator) Create(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error { - cmr := meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)) - csr := meta.ReferenceTo(cs, resource.MustGetKind(cs, a.typer)) - - mg.SetClaimReference(cmr) - mg.SetClassReference(csr) - if err := a.client.Create(ctx, mg); err != nil { - return errors.Wrap(err, errCreateManaged) - } - // Since we use GenerateName feature of ObjectMeta, final name of the - // resource is calculated during the creation of the resource. So, we - // can generate a complete reference only after the creation. - mgr := meta.ReferenceTo(mg, resource.MustGetKind(mg, a.typer)) - cm.SetResourceReference(mgr) - - return errors.Wrap(a.client.Update(ctx, cm), errUpdateClaim) -} - -// An APIBinder binds resources to claims by updating them in a Kubernetes API -// server. Note that APIBinder does not support objects using the status -// subresource; such objects should use APIStatusBinder. -type APIBinder struct { - client client.Client - typer runtime.ObjectTyper -} - -// NewAPIBinder returns a new APIBinder. -func NewAPIBinder(c client.Client, t runtime.ObjectTyper) *APIBinder { - return &APIBinder{client: c, typer: t} -} - -// Bind the supplied resource to the supplied claim. -func (a *APIBinder) Bind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - // A managed resource that was statically provisioned by an infrastructure - // operator should not have a controller reference. We assume a managed - // resource with a controller reference is part of a composite resource or a - // stack, and therefore not available to be claimed. - if metav1.GetControllerOf(mg) != nil { - return errors.New(errBindControlled) - } - - // Note that we allow a claim to bind to a managed resource with a nil claim - // reference in order to enable the static provisioning case in which a - // managed resource is provisioned ahead of time and is not associated with - // any claim. - if r := mg.GetClaimReference(); r != nil && !equal(meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)), r) { - return errors.New(errBindMismatch) - } - - cm.SetBindingPhase(v1alpha1.BindingPhaseBound) - - // This claim reference will already be set for dynamically provisioned - // managed resources, but we need to make sure it's set for statically - // provisioned resources too. - cmr := meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)) - mg.SetClaimReference(cmr) - mg.SetBindingPhase(v1alpha1.BindingPhaseBound) - if err := a.client.Update(ctx, mg); err != nil { - return errors.Wrap(err, errUpdateManaged) - } - - if meta.GetExternalName(mg) == "" { - return nil - } - - // Propagate back the final name of the external resource to the claim. - meta.SetExternalName(cm, meta.GetExternalName(mg)) - return errors.Wrap(a.client.Update(ctx, cm), errUpdateClaim) -} - -// Unbind the supplied Claim from the supplied Managed resource by removing the -// managed resource's claim reference, transitioning it to binding phase -// "Released", and if the managed resource's reclaim policy is "Delete", -// deleting it. -func (a *APIBinder) Unbind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - if !equal(meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)), mg.GetClaimReference()) { - return errors.New(errUnbindMismatch) - } - - mg.SetBindingPhase(v1alpha1.BindingPhaseReleased) - mg.SetClaimReference(nil) - if err := a.client.Update(ctx, mg); err != nil { - return errors.Wrap(resource.IgnoreNotFound(err), errUpdateManaged) - } - - // We go to the trouble of unbinding the managed resource before deleting it - // because we want it to show up as "released" (not "bound") if its managed - // resource reconciler is wedged or delayed trying to delete it. - if mg.GetReclaimPolicy() != v1alpha1.ReclaimDelete { - return nil - } - - return errors.Wrap(resource.IgnoreNotFound(a.client.Delete(ctx, mg)), errDeleteManaged) -} - -// An APIStatusBinder binds resources to claims by updating them in a -// Kubernetes API server. Note that APIStatusBinder does not support -// objects that do not use the status subresource; such objects should use -// APIBinder. -type APIStatusBinder struct { - client client.Client - typer runtime.ObjectTyper -} - -// NewAPIStatusBinder returns a new APIStatusBinder. -func NewAPIStatusBinder(c client.Client, t runtime.ObjectTyper) *APIStatusBinder { - return &APIStatusBinder{client: c, typer: t} -} - -// Bind the supplied resource to the supplied claim. -func (a *APIStatusBinder) Bind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - // A managed resource that was statically provisioned by an infrastructure - // operator should not have a controller reference. We assume a managed - // resource with a controller reference is part of a composite resource or a - // stack, and therefore not available to be claimed. - if metav1.GetControllerOf(mg) != nil { - return errors.New(errBindControlled) - } - - // Note that we allow a claim to bind to a managed resource with a nil claim - // reference in order to enable the static provisioning case in which a - // managed resource is provisioned ahead of time and is not associated with - // any claim. - if r := mg.GetClaimReference(); r != nil && !equal(meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)), r) { - return errors.New(errBindMismatch) - } - - cm.SetBindingPhase(v1alpha1.BindingPhaseBound) - - // This claim reference will already be set for dynamically provisioned - // managed resources, but we need to make sure it's set for statically - // provisioned resources too. - cmr := meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)) - mg.SetClaimReference(cmr) - if err := a.client.Update(ctx, mg); err != nil { - return errors.Wrap(err, errUpdateManaged) - } - - mg.SetBindingPhase(v1alpha1.BindingPhaseBound) - if err := a.client.Status().Update(ctx, mg); err != nil { - return errors.Wrap(err, errUpdateManagedStatus) - } - - if meta.GetExternalName(mg) == "" { - return nil - } - - // Propagate back the final name of the external resource to the claim. - meta.SetExternalName(cm, meta.GetExternalName(mg)) - return errors.Wrap(a.client.Update(ctx, cm), errUpdateClaim) -} - -// Unbind the supplied Claim from the supplied Managed resource by removing the -// managed resource's claim reference, transitioning it to binding phase -// "Released", and if the managed resource's reclaim policy is "Delete", -// deleting it. -func (a *APIStatusBinder) Unbind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - if !equal(meta.ReferenceTo(cm, resource.MustGetKind(cm, a.typer)), mg.GetClaimReference()) { - return errors.New(errUnbindMismatch) - } - - mg.SetClaimReference(nil) - if err := a.client.Update(ctx, mg); err != nil { - return errors.Wrap(resource.IgnoreNotFound(err), errUpdateManaged) - } - - mg.SetBindingPhase(v1alpha1.BindingPhaseReleased) - if err := a.client.Status().Update(ctx, mg); err != nil { - return errors.Wrap(resource.IgnoreNotFound(err), errUpdateManagedStatus) - } - - // We go to the trouble of unbinding the managed resource before deleting it - // because we want it to show up as "released" (not "bound") if its managed - // resource reconciler is wedged or delayed trying to delete it. - if mg.GetReclaimPolicy() != v1alpha1.ReclaimDelete { - return nil - } - - return errors.Wrap(resource.IgnoreNotFound(a.client.Delete(ctx, mg)), errDeleteManaged) -} - -// equal returns true if the supplied object references are considered equal. We -// consider two otherwise matching references with different UIDs to be equal, -// presuming that they are both references to a particular object that has been -// deleted and recreated, e.g. due to being restored from a backup. -// -// TODO(negz): If we switch to a reference that only has the fields we care -// about (GVK, namespace, and name) we can just use struct equality. -// https://github.com/crossplane/crossplane-runtime/issues/49 -func equal(a, b *corev1.ObjectReference) bool { - switch { - case a == nil || b == nil: - return a == b - case a.APIVersion != b.APIVersion: - return false - case a.Kind != b.Kind: - return false - case a.Namespace != b.Namespace: - return false - } - return a.Name == b.Name -} diff --git a/pkg/reconciler/claimbinding/api_test.go b/pkg/reconciler/claimbinding/api_test.go deleted file mode 100644 index 392a3b5..0000000 --- a/pkg/reconciler/claimbinding/api_test.go +++ /dev/null @@ -1,919 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" -) - -var ( - _ ManagedCreator = &APIManagedCreator{} - _ Binder = &APIBinder{} - _ Binder = &APIStatusBinder{} -) - -func TestCreate(t *testing.T) { - type fields struct { - client client.Client - typer runtime.ObjectTyper - } - - type args struct { - ctx context.Context - cm resource.Claim - cs resource.Class - mg resource.Managed - } - - cmname := "coolclaim" - csname := "coolclass" - mgname := "coolmanaged" - errBoom := errors.New("boom") - - cases := map[string]struct { - fields fields - args args - want error - }{ - "CreateManagedError": { - fields: fields{ - client: &test.MockClient{ - MockCreate: test.NewMockCreateFn(errBoom), - }, - typer: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: errors.Wrap(errBoom, errCreateManaged), - }, - "UpdateClaimError": { - fields: fields{ - client: &test.MockClient{ - MockCreate: test.NewMockCreateFn(nil), - MockUpdate: test.NewMockUpdateFn(errBoom), - }, - typer: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: errors.Wrap(errBoom, errUpdateClaim), - }, - "Successful": { - fields: fields{ - client: &test.MockClient{ - MockCreate: test.NewMockCreateFn(nil, func(got runtime.Object) error { - want := &fake.Managed{} - want.SetName(mgname) - want.SetClaimReference(&corev1.ObjectReference{ - Name: cmname, - APIVersion: fake.GVK(&fake.Claim{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Claim{}).Kind, - }) - want.SetClassReference(&corev1.ObjectReference{ - Name: csname, - APIVersion: fake.GVK(&fake.Class{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Class{}).Kind, - }) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - MockUpdate: test.NewMockUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetName(cmname) - want.SetResourceReference(&corev1.ObjectReference{ - Name: mgname, - APIVersion: fake.GVK(&fake.Managed{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Managed{}).Kind, - }) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - typer: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: cmname}}, - cs: &fake.Class{ObjectMeta: metav1.ObjectMeta{Name: csname}}, - mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Name: mgname}}, - }, - want: nil, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - api := NewAPIManagedCreator(tc.fields.client, tc.fields.typer) - err := api.Create(tc.args.ctx, tc.args.cm, tc.args.cs, tc.args.mg) - if diff := cmp.Diff(tc.want, err, test.EquateErrors()); diff != "" { - t.Errorf("api.Create(...): -want error, +got error:\n%s", diff) - } - }) - } -} - -func TestBind(t *testing.T) { - type args struct { - ctx context.Context - cm resource.Claim - mg resource.Managed - } - - type want struct { - err error - cm resource.Claim - mg resource.Managed - } - - errBoom := errors.New("boom") - externalName := "very-cool-external-resource" - controller := true - - cases := map[string]struct { - client client.Client - typer runtime.ObjectTyper - args args - want want - }{ - "ControlledError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{{Controller: &controller}}, - }}, - }, - want: want{ - err: errors.New(errBindControlled), - cm: &fake.Claim{}, - mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{{Controller: &controller}}, - }}, - }, - }, - "RefMismatchError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - }, - }, - want: want{ - err: errors.New(errBindMismatch), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - }, - }, - }, - "UpdateManagedError": { - client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - switch obj.(type) { - case *fake.Managed: - return errBoom - default: - return errors.New("unexpected object kind") - } - })}, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{}, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManaged), - cm: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "UpdateClaimError": { - client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - switch obj.(type) { - case *fake.Managed: - return nil - case *fake.Claim: - return errBoom - default: - return errors.New("unexpected object kind") - } - })}, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateClaim), - cm: &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "SuccessfulWithoutExternalName": { - client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)}, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{}, - }, - want: want{ - err: nil, - cm: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "SuccessfulWithExternalName": { - client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)}, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - }, - }, - want: want{ - err: nil, - cm: &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - api := NewAPIBinder(tc.client, tc.typer) - err := api.Bind(tc.args.ctx, tc.args.cm, tc.args.mg) - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("api.Bind(...): -want error, +got error:\n%s", diff) - } - if diff := cmp.Diff(tc.want.cm, tc.args.cm, test.EquateConditions()); diff != "" { - t.Errorf("api.Bind(...) Claim: -want, +got:\n%s", diff) - } - if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" { - t.Errorf("api.Bind(...) Managed: -want, +got:\n%s", diff) - } - }) - } -} - -func TestStatusBind(t *testing.T) { - type args struct { - ctx context.Context - cm resource.Claim - mg resource.Managed - } - - type want struct { - err error - cm resource.Claim - mg resource.Managed - } - - errBoom := errors.New("boom") - externalName := "very-cool-external-resource" - controller := true - - cases := map[string]struct { - client client.Client - typer runtime.ObjectTyper - args args - want want - }{ - "ControlledError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{{Controller: &controller}}, - }}, - }, - want: want{ - err: errors.New(errBindControlled), - cm: &fake.Claim{}, - mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{{Controller: &controller}}, - }}, - }, - }, - "RefMismatchError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - }, - }, - want: want{ - err: errors.New(errBindMismatch), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - }, - }, - }, - "UpdateManagedError": { - client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - switch obj.(type) { - case *fake.Managed: - return errBoom - default: - return errors.New("unexpected object kind") - } - })}, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{}, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManaged), - cm: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - }, - }, - }, - "UpdateManagedStatusError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(errBoom), - }, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{}, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManagedStatus), - cm: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "UpdateClaimError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - switch obj.(type) { - case *fake.Managed: - return nil - case *fake.Claim: - return errBoom - default: - return errors.New("unexpected object kind") - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - }, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateClaim), - cm: &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "SuccessfulWithoutExternalName": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - }, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{}, - }, - want: want{ - err: nil, - cm: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - mg: &fake.Managed{ - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - "SuccessfulWithExternalName": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - }, - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - }, - }, - want: want{ - err: nil, - cm: &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}}, - ClaimReferencer: fake.ClaimReferencer{Ref: meta.ReferenceTo(&fake.Claim{}, fake.GVK(&fake.Claim{}))}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - api := NewAPIStatusBinder(tc.client, tc.typer) - err := api.Bind(tc.args.ctx, tc.args.cm, tc.args.mg) - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("api.Bind(...): -want error, +got error:\n%s", diff) - } - if diff := cmp.Diff(tc.want.cm, tc.args.cm, test.EquateConditions()); diff != "" { - t.Errorf("api.Bind(...) Claim: -want, +got:\n%s", diff) - } - if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" { - t.Errorf("api.Bind(...) Managed: -want, +got:\n%s", diff) - } - }) - } -} - -func TestUnbind(t *testing.T) { - type args struct { - ctx context.Context - cm resource.Claim - mg resource.Managed - } - - type want struct { - err error - mg resource.Managed - } - - errBoom := errors.New("boom") - - typer := fake.SchemeWith(&fake.Claim{}) - ref := meta.ReferenceTo(&fake.Claim{}, resource.MustGetKind(&fake.Claim{}, typer)) - - cases := map[string]struct { - client client.Client - typer runtime.ObjectTyper - args args - want want - }{ - "RefMismatchError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ClaimReferencer: fake.ClaimReferencer{Ref: ref}}, - }, - want: want{ - err: errors.New(errUnbindMismatch), - mg: &fake.Managed{ClaimReferencer: fake.ClaimReferencer{Ref: ref}}, - }, - }, - "SuccessfulRetain": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: nil, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "SuccessfulDelete": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockDelete: test.NewMockDeleteFn(nil), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: nil, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "UpdateError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(errBoom), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManaged), - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "DeleteError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockDelete: test.NewMockDeleteFn(errBoom), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errDeleteManaged), - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - api := NewAPIBinder(tc.client, tc.typer) - err := api.Unbind(tc.args.ctx, tc.args.cm, tc.args.mg) - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("api.Unbind(...): -want error, +got error:\n%s", diff) - } - if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" { - t.Errorf("api.Unbind(...) Managed: -want, +got:\n%s", diff) - } - }) - } -} - -func TestStatusUnbind(t *testing.T) { - type args struct { - ctx context.Context - cm resource.Claim - mg resource.Managed - } - - type want struct { - err error - mg resource.Managed - } - - errBoom := errors.New("boom") - typer := fake.SchemeWith(&fake.Claim{}) - ref := meta.ReferenceTo(&fake.Claim{}, resource.MustGetKind(&fake.Claim{}, typer)) - - cases := map[string]struct { - client client.Client - typer runtime.ObjectTyper - args args - want want - }{ - "RefMismatchError": { - typer: fake.SchemeWith(&fake.Claim{}), - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Name: "I'm different!"}}, - mg: &fake.Managed{ClaimReferencer: fake.ClaimReferencer{Ref: ref}}, - }, - want: want{ - err: errors.New(errUnbindMismatch), - mg: &fake.Managed{ClaimReferencer: fake.ClaimReferencer{Ref: ref}}, - }, - }, - "SuccessfulRetain": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: nil, - mg: &fake.Managed{ - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "SuccessfulDelete": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - MockDelete: test.NewMockDeleteFn(nil), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: nil, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "UpdateError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(errBoom), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManaged), - mg: &fake.Managed{ - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "UpdateStatusError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(errBoom), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errUpdateManagedStatus), - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - "DeleteError": { - client: &test.MockClient{ - MockUpdate: test.NewMockUpdateFn(nil), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil), - MockDelete: test.NewMockDeleteFn(errBoom), - }, - typer: typer, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}, - ClaimReferencer: fake.ClaimReferencer{Ref: ref}, - }, - }, - want: want{ - err: errors.Wrap(errBoom, errDeleteManaged), - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}, - ClaimReferencer: fake.ClaimReferencer{Ref: nil}, - }, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - api := NewAPIStatusBinder(tc.client, tc.typer) - err := api.Unbind(tc.args.ctx, tc.args.cm, tc.args.mg) - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("api.Unbind(...): -want error, +got error:\n%s", diff) - } - if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" { - t.Errorf("api.Unbind(...) Managed: -want, +got:\n%s", diff) - } - }) - } -} - -func TestObjectReferenceEqual(t *testing.T) { - cases := map[string]struct { - a *corev1.ObjectReference - b *corev1.ObjectReference - want bool - }{ - "BothNil": { - want: true, - }, - "OneIsNil": { - a: &corev1.ObjectReference{}, - want: false, - }, - "MismatchedAPIVersion": { - a: &corev1.ObjectReference{ - APIVersion: "v", - }, - b: &corev1.ObjectReference{}, - want: false, - }, - "MismatchedKind": { - a: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - }, - b: &corev1.ObjectReference{ - APIVersion: "v", - }, - want: false, - }, - "MismatchedNamespace": { - a: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - Namespace: "ns", - }, - b: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - }, - want: false, - }, - "MismatchedName": { - a: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - Namespace: "ns", - Name: "cool", - }, - b: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - Namespace: "ns", - }, - want: false, - }, - "Match": { - a: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - Namespace: "ns", - Name: "cool", - }, - b: &corev1.ObjectReference{ - APIVersion: "v", - Kind: "k", - Namespace: "ns", - Name: "cool", - }, - want: true, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := equal(tc.a, tc.b) - if got != tc.want { - t.Errorf("equal(...): want %t, got %t", tc.want, got) - } - }) - } -} diff --git a/pkg/reconciler/claimbinding/configurator.go b/pkg/reconciler/claimbinding/configurator.go deleted file mode 100644 index 6a37fe5..0000000 --- a/pkg/reconciler/claimbinding/configurator.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" -) - -// A ConfiguratorChain chains multiple configurators. -type ConfiguratorChain []ManagedConfigurator - -// Configure calls each ManagedConfigurator serially. It returns the first -// error it encounters, if any. -func (cc ConfiguratorChain) Configure(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error { - for _, c := range cc { - if err := c.Configure(ctx, cm, cs, mg); err != nil { - return err - } - } - return nil -} - -// An ObjectMetaConfigurator sets standard object metadata for a dynamically -// provisioned resource, deriving it from a class and claim. It is deprecated; -// use ConfigureNames instead. -type ObjectMetaConfigurator struct{} - -// NewObjectMetaConfigurator returns a new ObjectMetaConfigurator. -func NewObjectMetaConfigurator(_ runtime.ObjectTyper) *ObjectMetaConfigurator { - return &ObjectMetaConfigurator{} -} - -// Configure the supplied Managed resource's object metadata. -func (c *ObjectMetaConfigurator) Configure(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error { - return ConfigureNames(ctx, cm, cs, mg) -} - -// ConfigureNames configures the name and external name of the supplied managed -// resource. The managed resource name is derived from the supplied resource -// claim, in the form {claim-namespace}-{claim-name}-{random-string}. The -// resource claim's external name annotation, if any, is propagated to the -// managed resource. -func ConfigureNames(_ context.Context, cm resource.Claim, _ resource.Class, mg resource.Managed) error { - mg.SetGenerateName(fmt.Sprintf("%s-%s-", cm.GetNamespace(), cm.GetName())) - if meta.GetExternalName(cm) != "" { - meta.SetExternalName(mg, meta.GetExternalName(cm)) - } - - return nil -} - -// ConfigureReclaimPolicy configures the reclaim policy of the supplied managed -// resource. If the managed resource _already has_ a reclaim policy (for example -// because one was set by another configurator) it is respected. Otherwise the -// reclaim policy is copied from the resource class. If the resource class does -// not specify a reclaim policy, the managed resource's policy is set to -// "Delete". -func ConfigureReclaimPolicy(_ context.Context, _ resource.Claim, cs resource.Class, mg resource.Managed) error { - if mg.GetReclaimPolicy() != "" { - return nil - } - - mg.SetReclaimPolicy(cs.GetReclaimPolicy()) - - if mg.GetReclaimPolicy() == "" { - mg.SetReclaimPolicy(v1alpha1.ReclaimDelete) - } - - return nil -} diff --git a/pkg/reconciler/claimbinding/configurator_test.go b/pkg/reconciler/claimbinding/configurator_test.go deleted file mode 100644 index 83869f1..0000000 --- a/pkg/reconciler/claimbinding/configurator_test.go +++ /dev/null @@ -1,247 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" -) - -var ( - _ ManagedConfiguratorFn = ConfigureNames - _ ManagedConfiguratorFn = ConfigureReclaimPolicy - _ ManagedConfigurator = ConfiguratorChain{} -) - -func TestConfiguratorChain(t *testing.T) { - errBoom := errors.New("boom") - - type args struct { - ctx context.Context - cm resource.Claim - cs resource.Class - mg resource.Managed - } - - cases := map[string]struct { - cc ConfiguratorChain - args args - want error - }{ - "EmptyChain": { - cc: ConfiguratorChain{}, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: nil, - }, - "SuccessulConfigurator": { - cc: ConfiguratorChain{ - ManagedConfiguratorFn(func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { - return nil - }), - }, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: nil, - }, - "ConfiguratorReturnsError": { - cc: ConfiguratorChain{ - ManagedConfiguratorFn(func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { - return errBoom - }), - }, - args: args{ - ctx: context.Background(), - cm: &fake.Claim{}, - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: errBoom, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := tc.cc.Configure(tc.args.ctx, tc.args.cm, tc.args.cs, tc.args.mg) - if diff := cmp.Diff(tc.want, got, test.EquateErrors()); diff != "" { - t.Errorf("tc.cc.Configure(...): -want error, +got error:\n%s", diff) - } - }) - } -} - -func TestNameConfigurators(t *testing.T) { - claimName := "myclaim" - claimNS := "myclaimns" - externalName := "wayout" - - type args struct { - ctx context.Context - cm resource.Claim - cs resource.Class - mg resource.Managed - } - - type want struct { - err error - mg resource.Managed - } - - cases := map[string]struct { - args args - want want - }{ - "Successful": { - args: args{ - ctx: context.Background(), - cm: &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: claimNS, - Name: claimName, - Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}, - }}, - mg: &fake.Managed{}, - }, - want: want{ - mg: &fake.Managed{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: claimNS + "-" + claimName + "-", - Annotations: map[string]string{meta.AnnotationKeyExternalName: externalName}, - }, - }, - }, - }, - } - - t.Run("TestConfigureNames", func(t *testing.T) { - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := ConfigureNames(tc.args.ctx, tc.args.cm, tc.args.cs, tc.args.mg) - if diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != "" { - t.Errorf("ConfigureNames(...): -want error, +got error:\n%s", diff) - } - - if diff := cmp.Diff(tc.want.mg, tc.args.mg); diff != "" { - t.Errorf("ConfigureNames(...) Managed: -want, +got error:\n%s", diff) - } - }) - } - }) - - // NOTE(negz): This deprecated API wraps ConfigureNames; they should behave - // identically. - t.Run("TestObjectMetaConfigurator", func(t *testing.T) { - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - om := NewObjectMetaConfigurator(nil) - got := om.Configure(tc.args.ctx, tc.args.cm, tc.args.cs, tc.args.mg) - if diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != "" { - t.Errorf("om.Configure(...): -want error, +got error:\n%s", diff) - } - - if diff := cmp.Diff(tc.want.mg, tc.args.mg); diff != "" { - t.Errorf("om.Configure(...) Managed: -want, +got error:\n%s", diff) - } - }) - } - }) -} - -func TestConfigureReclaimPolicy(t *testing.T) { - type args struct { - ctx context.Context - cm resource.Claim - cs resource.Class - mg resource.Managed - } - - type want struct { - err error - mg resource.Managed - } - - cases := map[string]struct { - reason string - args args - want want - }{ - "AlreadySet": { - reason: "Existing managed resource reclaim policies should be respected.", - args: args{ - ctx: context.Background(), - cs: &fake.Class{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}}, - mg: &fake.Managed{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}}, - }, - want: want{ - mg: &fake.Managed{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}}, - }, - }, - "SetByClass": { - reason: "The class's reclaim policy should be propagated to the managed resource.", - args: args{ - ctx: context.Background(), - cs: &fake.Class{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}}, - mg: &fake.Managed{}, - }, - want: want{ - mg: &fake.Managed{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}}, - }, - }, - "DefaultToDelete": { - reason: "If neither the class nor managed resource set a reclaim policy, it should default to Delete.", - args: args{ - ctx: context.Background(), - cs: &fake.Class{}, - mg: &fake.Managed{}, - }, - want: want{ - mg: &fake.Managed{Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}}, - }, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := ConfigureReclaimPolicy(tc.args.ctx, tc.args.cm, tc.args.cs, tc.args.mg) - if diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != "" { - t.Errorf("\nReason: %s\nConfigureReclaimPolicy(...): -want error, +got error:\n%s", tc.reason, diff) - } - - if diff := cmp.Diff(tc.want.mg, tc.args.mg); diff != "" { - t.Errorf("\nReason: %s\nConfigureReclaimPolicy(...) Managed: -want, +got error:\n%s", tc.reason, diff) - } - }) - } -} diff --git a/pkg/reconciler/claimbinding/doc.go b/pkg/reconciler/claimbinding/doc.go deleted file mode 100644 index c565464..0000000 --- a/pkg/reconciler/claimbinding/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -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 claimbinding provides a resource claim binding reconciler. -// Deprecated: See https://github.com/crossplane/crossplane/issues/1670 -package claimbinding diff --git a/pkg/reconciler/claimbinding/reconciler.go b/pkg/reconciler/claimbinding/reconciler.go deleted file mode 100644 index 80dc7ed..0000000 --- a/pkg/reconciler/claimbinding/reconciler.go +++ /dev/null @@ -1,514 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - "strings" - "time" - - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/event" - "github.com/crossplane/crossplane-runtime/pkg/logging" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" -) - -const ( - claimFinalizerName = "finalizer.resourceclaim.crossplane.io" - claimReconcileTimeout = 1 * time.Minute - - aShortWait = 30 * time.Second -) - -// Reasons a resource claim is or is not ready. -const ( - ReasonBinding = "Managed claim is waiting for managed resource to become bindable" -) - -// Error strings. -const ( - errGetClaim = "cannot get resource claim" - errUpdateClaimStatus = "cannot update resource claim status" -) - -// Event reasons. -const ( - reasonCannotGetResource event.Reason = "CannotGetManagedResource" - reasonCannotGetClass event.Reason = "CannotGetResourceClass" - reasonCannotConfigureResource event.Reason = "CannotConfigureManagedResource" - reasonCannotCreateResource event.Reason = "CannotCreateManagedResource" - reasonCannotPropagate event.Reason = "CannotPropagateConnectionDetails" - reasonCannotBind event.Reason = "CannotBindManagedResource" - reasonCannotUnbind event.Reason = "CannotUnbindManagedResource" - - reasonResourceNotFound event.Reason = "ManagedResourceNotFound" - reasonCreatedResource event.Reason = "CreatedManagedResource" - reasonWaitingToBind event.Reason = "WaitingToBind" - reasonBound event.Reason = "BoundManagedResource" - reasonUnbound event.Reason = "UnboundManagedResource" -) - -// ControllerName returns the recommended name for controllers that use this -// package to reconcile a particular kind of resource claim. -func ControllerName(kind string) string { - return "claimbinding/" + strings.ToLower(kind) -} - -// A ManagedConfigurator configures a resource, typically by converting it to -// a known type and populating its spec. -type ManagedConfigurator interface { - Configure(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error -} - -// A ManagedConfiguratorFn is a function that satisfies the -// ManagedConfigurator interface. -type ManagedConfiguratorFn func(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error - -// Configure the supplied resource using the supplied claim and class. -func (fn ManagedConfiguratorFn) Configure(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error { - return fn(ctx, cm, cs, mg) -} - -// A ManagedCreator creates a resource, typically by submitting it to an API -// server. ManagedCreators must not modify the supplied resource class, but are -// responsible for final modifications to the claim and resource, for example -// ensuring resource, class, claim, and owner references are set. -type ManagedCreator interface { - Create(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error -} - -// A ManagedCreatorFn is a function that satisfies the ManagedCreator interface. -type ManagedCreatorFn func(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error - -// Create the supplied resource. -func (fn ManagedCreatorFn) Create(ctx context.Context, cm resource.Claim, cs resource.Class, mg resource.Managed) error { - return fn(ctx, cm, cs, mg) -} - -// A Binder binds a resource claim to a managed resource. -type Binder interface { - // Bind the supplied Claim to the supplied Managed resource. - Bind(ctx context.Context, cm resource.Claim, mg resource.Managed) error - - // Unbind the supplied Claim from the supplied Managed resource. - Unbind(ctx context.Context, cm resource.Claim, mg resource.Managed) error -} - -// BinderFns satisfy the Binder interface. -type BinderFns struct { - BindFn func(ctx context.Context, cm resource.Claim, mg resource.Managed) error - UnbindFn func(ctx context.Context, cm resource.Claim, mg resource.Managed) error -} - -// Bind the supplied Claim to the supplied Managed resource. -func (b BinderFns) Bind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - return b.BindFn(ctx, cm, mg) -} - -// Unbind the supplied Claim from the supplied Managed resource. -func (b BinderFns) Unbind(ctx context.Context, cm resource.Claim, mg resource.Managed) error { - return b.UnbindFn(ctx, cm, mg) -} - -// A Reconciler reconciles resource claims by creating exactly one kind of -// concrete managed resource. Each resource claim kind should create an instance -// of this controller for each managed resource kind they can bind to, using -// watch predicates to ensure each controller is responsible for exactly one -// type of resource class provisioner. Each controller must watch its subset of -// resource claims and any managed resources they control. -type Reconciler struct { - client client.Client - newClaim func() resource.Claim - newClass func() resource.Class - newManaged func() resource.Managed - - // The below structs embed the set of interfaces used to implement the - // resource claim reconciler. We do this primarily for readability, so that - // the reconciler logic reads r.managed.Create(), r.claim.Finalize(), etc. - managed crManaged - claim crClaim - - log logging.Logger - record event.Recorder -} - -type crManaged struct { - ManagedConfigurator - ManagedCreator - resource.ManagedConnectionPropagator -} - -func defaultCRManaged(m manager.Manager) crManaged { - return crManaged{ - ManagedConfigurator: ConfiguratorChain{ - ManagedConfiguratorFn(ConfigureNames), - ManagedConfiguratorFn(ConfigureReclaimPolicy), - }, - ManagedCreator: NewAPIManagedCreator(m.GetClient(), m.GetScheme()), - - // TODO(negz): Switch to ConnectionPropagator once this has been - // deprecated for a release or two. - //nolint:staticcheck - ManagedConnectionPropagator: resource.NewAPIManagedConnectionPropagator(m.GetClient(), m.GetScheme()), - } -} - -type crClaim struct { - resource.Finalizer - Binder -} - -func defaultCRClaim(m manager.Manager) crClaim { - return crClaim{ - Finalizer: resource.NewAPIFinalizer(m.GetClient(), claimFinalizerName), - Binder: NewAPIStatusBinder(m.GetClient(), m.GetScheme()), - } -} - -// A ReconcilerOption configures a Reconciler. -type ReconcilerOption func(*Reconciler) - -// WithManagedConfigurators specifies which configurators should be used to -// configure each managed resource. Configurators will be applied in the order -// they are specified. -func WithManagedConfigurators(c ...ManagedConfigurator) ReconcilerOption { - return func(r *Reconciler) { - r.managed.ManagedConfigurator = ConfiguratorChain(c) - } -} - -// WithManagedCreator specifies which ManagedCreator should be used to create -// managed resources. -func WithManagedCreator(c ManagedCreator) ReconcilerOption { - return func(r *Reconciler) { - r.managed.ManagedCreator = c - } -} - -// WithManagedConnectionPropagator specifies which ManagedConnectionPropagator -// should be used to propagate resource connection details to their claim. -// -// Deprecated: Use WithConnectionPropagator. -func WithManagedConnectionPropagator(p resource.ManagedConnectionPropagator) ReconcilerOption { - return func(r *Reconciler) { - r.managed.ManagedConnectionPropagator = p - } -} - -// WithConnectionPropagator specifies which ConnectionPropagator -// should be used to propagate resource connection details to their claim. -func WithConnectionPropagator(p resource.ConnectionPropagator) ReconcilerOption { - return func(r *Reconciler) { - r.managed.ManagedConnectionPropagator = &resource.APIManagedConnectionPropagator{Propagator: p} - } -} - -// WithBinder specifies which Binder should be used to bind -// resources to their claim. -func WithBinder(b Binder) ReconcilerOption { - return func(r *Reconciler) { - r.claim.Binder = b - } -} - -// WithClaimFinalizer specifies which ClaimFinalizer should be used to finalize -// claims when they are deleted. -func WithClaimFinalizer(f resource.Finalizer) ReconcilerOption { - return func(r *Reconciler) { - r.claim.Finalizer = f - } -} - -// WithLogger specifies how the Reconciler should log messages. -func WithLogger(l logging.Logger) ReconcilerOption { - return func(r *Reconciler) { - r.log = l - } -} - -// WithRecorder specifies how the Reconciler should record events. -func WithRecorder(er event.Recorder) ReconcilerOption { - return func(r *Reconciler) { - r.record = er - } -} - -// NewReconciler returns a Reconciler that reconciles resource claims -// of the supplied ClaimKind with resources of the supplied ManagedKind. It -// panics if asked to reconcile a claim or resource kind that is not registered -// with the supplied manager's runtime.Scheme. The returned Reconciler will -// apply only the ObjectMetaConfigurator by default; most callers should supply -// one or more ManagedConfigurators to configure their managed resources. -func NewReconciler(m manager.Manager, of resource.ClaimKind, using resource.ClassKind, with resource.ManagedKind, o ...ReconcilerOption) *Reconciler { - nc := func() resource.Claim { - return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Claim) - } - ns := func() resource.Class { - return resource.MustCreateObject(schema.GroupVersionKind(using), m.GetScheme()).(resource.Class) - } - nr := func() resource.Managed { - return resource.MustCreateObject(schema.GroupVersionKind(with), m.GetScheme()).(resource.Managed) - } - - // Panic early if we've been asked to reconcile a claim or resource kind - // that has not been registered with our controller manager's scheme. - _, _, _ = nc(), ns(), nr() - - r := &Reconciler{ - client: m.GetClient(), - newClaim: nc, - newClass: ns, - newManaged: nr, - managed: defaultCRManaged(m), - claim: defaultCRClaim(m), - log: logging.NewNopLogger(), - record: event.NewNopRecorder(), - } - - for _, ro := range o { - ro(r) - } - - return r -} - -// Reconcile a resource claim with a concrete managed resource. -func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) { // nolint:gocyclo - // NOTE(negz): This method is well over our cyclomatic complexity goal. - // Be wary of adding additional complexity. - - log := r.log.WithValues("request", req) - log.Debug("Reconciling") - - ctx, cancel := context.WithTimeout(context.Background(), claimReconcileTimeout) - defer cancel() - - claim := r.newClaim() - if err := r.client.Get(ctx, req.NamespacedName, claim); err != nil { - // There's no need to requeue if we no longer exist. Otherwise we'll be - // requeued implicitly because we return an error. - log.Debug("Cannot get resource claim", "error", err) - return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetClaim) - } - - record := r.record.WithAnnotations("external-name", meta.GetExternalName(claim)) - log = log.WithValues( - "uid", claim.GetUID(), - "version", claim.GetResourceVersion(), - "external-name", meta.GetExternalName(claim), - ) - - managed := r.newManaged() - if ref := claim.GetResourceReference(); ref != nil { - record = record.WithAnnotations("managed-name", claim.GetResourceReference().Name) - log = log.WithValues("managed-name", claim.GetResourceReference().Name) - - err := r.client.Get(ctx, meta.NamespacedNameOf(ref), managed) - if kerrors.IsNotFound(err) { - // If the managed resource we explicitly reference doesn't exist yet - // we want to retry after a brief wait, in case it is created. We - // must explicitly requeue because our EnqueueRequestForClaim - // handler can only enqueue reconciles for managed resources that - // have their claim reference set, so we can't expect to be queued - // implicitly when the managed resource we want to bind to appears. - log.Debug("Referenced managed resource not found", "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Normal(reasonResourceNotFound, "Referenced managed resource not found")) - claim.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - if err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error. - log.Debug("Cannot get referenced managed resource", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotGetResource, err)) - claim.SetConditions(v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - } - - if meta.WasDeleted(claim) { - log = log.WithValues("deletion-timestamp", claim.GetDeletionTimestamp()) - - if err := r.claim.Unbind(ctx, claim, managed); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error. - log.Debug("Cannot unbind claim", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotUnbind, err)) - claim.SetConditions(v1alpha1.Deleting(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - log.Debug("Successfully unbound managed resource") - record.Event(claim, event.Normal(reasonUnbound, "Successfully unbound managed resource")) - - if err := r.claim.RemoveFinalizer(ctx, claim); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error. - log.Debug("Cannot remove finalizer", "error", err, "requeue-after", time.Now().Add(aShortWait)) - claim.SetConditions(v1alpha1.Deleting(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - // We've successfully deleted our claim and removed our finalizer. If we - // assume we were the only controller that added a finalizer to this - // claim then it should no longer exist and thus there is no point - // trying to update its status. - log.Debug("Successfully deleted resource claim") - return reconcile.Result{Requeue: false}, nil - } - - if err := r.claim.AddFinalizer(ctx, claim); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error. - log.Debug("Cannot add resource claim finalizer", "error", err, "requeue-after", time.Now().Add(aShortWait)) - claim.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - // Claim reconcilers (should) watch for either claims with a resource ref, - // claims with a class ref, or managed resources with a claim ref. In the - // first case the managed resource always exists by the time we get here. In - // the second case the class reference is set. The third case exposes us to - // a pathological scenario in which a managed resource references a claim - // that has no resource ref or class ref, so we can't assume the class ref - // is always set at this point. - if !meta.WasCreated(managed) && claim.GetClassReference() != nil { - record = record.WithAnnotations("class-name", claim.GetClassReference().Name) - log = log.WithValues("class-name", claim.GetClassReference().Name) - - class := r.newClass() - // Class reference should always be set by the time we get this far; we - // set it on last reconciliation. - if err := r.client.Get(ctx, meta.NamespacedNameOf(claim.GetClassReference()), class); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error or the - // class is (re)created. - log.Debug("Cannot get referenced resource class", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotGetClass, err)) - claim.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - if err := r.managed.Configure(ctx, claim, class, managed); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error or some - // issue with the resource class was resolved. - log.Debug("Cannot configure managed resource", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotConfigureResource, err)) - claim.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - // We'll know our managed resource's name at this point because it was - // set by the above configure step. - record = record.WithAnnotations("managed-name", managed.GetName()) - log = log.WithValues("managed-name", managed.GetName()) - - if err := r.managed.Create(ctx, claim, class, managed); err != nil { - // If we didn't hit this error last time we'll be requeued - // implicitly due to the status update. Otherwise we want to retry - // after a brief wait, in case this was a transient error. - log.Debug("Cannot create managed resource", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotCreateResource, err)) - claim.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - log.Debug("Successfully created managed resource") - record.Event(claim, event.Normal(reasonCreatedResource, "Successfully created managed resource")) - } - - if !resource.IsBindable(managed) && !resource.IsBound(managed) { - log.Debug("Managed resource is not yet bindable") - record.Event(claim, event.Normal(reasonWaitingToBind, "Managed resource is not yet bindable")) - - if managed.GetClaimReference() == nil { - // We're waiting to bind to a statically provisioned managed - // resource. We must requeue because our EnqueueRequestForClaim - // handler can only enqueue reconciles for managed resource updates - // when they have their claim reference set, and that doesn't happen - // until we bind to the managed resource we're waiting for. - claim.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - // If this claim was not already binding we'll be requeued due to the - // status update. Otherwise there's no need to requeue. We should be - // watching both the resource claims and the resources we own, so we'll - // be queued if anything changes. - claim.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - if resource.IsBindable(managed) { - if err := r.managed.PropagateConnection(ctx, claim, managed); err != nil { - // If we didn't hit this error last time we'll be requeued implicitly - // due to the status update. Otherwise we want to retry after a brief - // wait in case this was a transient error, or the resource connection - // secret is created. - log.Debug("Cannot propagate connection details from managed resource to claim", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotPropagate, err)) - claim.SetConditions(Binding(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - if err := r.claim.Bind(ctx, claim, managed); err != nil { - // If we didn't hit this error last time we'll be requeued implicitly - // due to the status update. Otherwise we want to retry after a brief - // wait, in case this was a transient error. - log.Debug("Cannot bind to managed resource", "error", err, "requeue-after", time.Now().Add(aShortWait)) - record.Event(claim, event.Warning(reasonCannotBind, err)) - claim.SetConditions(Binding(), v1alpha1.ReconcileError(err)) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) - } - - log.Debug("Successfully bound managed resource") - record.Event(claim, event.Normal(reasonBound, "Successfully bound managed resource")) - } - - // No need to requeue. We should be watching both the resource claims and - // the resources we own, so we'll be queued if anything changes. - claim.SetConditions(v1alpha1.Available(), v1alpha1.ReconcileSuccess()) - return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Status().Update(ctx, claim), errUpdateClaimStatus) -} - -// Binding returns a condition that indicates the resource claim is currently -// waiting for its managed resource to become bindable. -func Binding() v1alpha1.Condition { - return v1alpha1.Condition{ - Type: v1alpha1.TypeReady, - Status: corev1.ConditionFalse, - LastTransitionTime: metav1.Now(), - Reason: ReasonBinding, - } -} diff --git a/pkg/reconciler/claimbinding/reconciler_test.go b/pkg/reconciler/claimbinding/reconciler_test.go deleted file mode 100644 index 847f573..0000000 --- a/pkg/reconciler/claimbinding/reconciler_test.go +++ /dev/null @@ -1,735 +0,0 @@ -/* -Copyright 2019 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 claimbinding - -import ( - "context" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" -) - -var _ reconcile.Reconciler = &Reconciler{} - -func TestReconciler(t *testing.T) { - type args struct { - m manager.Manager - of resource.ClaimKind - use resource.ClassKind - with resource.ManagedKind - o []ReconcilerOption - } - - type want struct { - result reconcile.Result - err error - } - - errBoom := errors.New("boom") - errUnexpected := errors.New("unexpected object type") - now := metav1.Now() - - cases := map[string]struct { - args args - want want - }{ - "GetClaimError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)}, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - }, - want: want{err: errors.Wrap(errBoom, errGetClaim)}, - }, - "GetManagedError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - return errBoom - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "ManagedNotFound": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - return kerrors.NewNotFound(schema.GroupResource{}, "") - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "UnbindError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetDeletionTimestamp(&now) - *o = *cm - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetDeletionTimestamp(&now) - want.SetConditions(v1alpha1.Deleting(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return errBoom }}), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "UnbindSuccess": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetDeletionTimestamp(&now) - *o = *cm - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetDeletionTimestamp(&now) - want.SetConditions(v1alpha1.Deleting(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}), - WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "RemoveClaimFinalizerError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetDeletionTimestamp(&now) - *o = *cm - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetDeletionTimestamp(&now) - want.SetConditions(v1alpha1.Deleting(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}), - WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "SuccessfulDelete": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetDeletionTimestamp(&now) - *o = *cm - return nil - default: - return errUnexpected - } - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}), - WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "ClassReferenceNotSet": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - *o = fake.Claim{} - return nil - case *fake.Managed: - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "GetResourceClassError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetClassReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Class: - return errBoom - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetClassReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "ConfigureManagedError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetClassReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Class: - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetClassReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithManagedConfigurators(ManagedConfiguratorFn( - func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { return errBoom }, - )), - WithClaimFinalizer(resource.FinalizerFns{ - AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}, - ), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "CreateManagedError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetClassReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Class: - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetClassReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithManagedConfigurators(ManagedConfiguratorFn( - func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { return nil }, - )), - WithClaimFinalizer(resource.FinalizerFns{ - AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}, - ), - WithManagedCreator(ManagedCreatorFn( - func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { return errBoom }, - )), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "ManagedIsInUnknownBindingPhase": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - // We do not explicitly set a BindingPhase here - // because the zero value of BindingPhase is - // BindingPhaseUnset. - mg := &fake.Managed{} - mg.SetClaimReference(&corev1.ObjectReference{}) - mg.SetCreationTimestamp(now) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "ManagedIsInUnbindableBindingPhase": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - mg := &fake.Managed{} - mg.SetCreationTimestamp(now) - mg.SetClaimReference(&corev1.ObjectReference{}) - mg.SetBindingPhase(v1alpha1.BindingPhaseUnbindable) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(Binding(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "PropagateConnectionError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - mg := &fake.Managed{} - mg.SetCreationTimestamp(now) - mg.SetBindingPhase(v1alpha1.BindingPhaseUnbound) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(Binding(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - WithManagedConnectionPropagator(resource.ManagedConnectionPropagatorFn( - func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.Managed) error { - return errBoom - }, - )), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "AddFinalizerError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - mg := &fake.Managed{} - mg.SetCreationTimestamp(now) - mg.SetBindingPhase(v1alpha1.BindingPhaseUnbound) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.Creating(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithManagedConnectionPropagator(resource.ManagedConnectionPropagatorFn( - func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.Managed) error { return nil }, - )), - WithClaimFinalizer(resource.FinalizerFns{ - AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}, - ), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "BindError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - mg := &fake.Managed{} - mg.SetCreationTimestamp(now) - mg.SetBindingPhase(v1alpha1.BindingPhaseUnbound) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(Binding(), v1alpha1.ReconcileError(errBoom)) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithManagedConnectionPropagator(resource.ManagedConnectionPropagatorFn( - func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.Managed) error { return nil }, - )), - WithClaimFinalizer(resource.FinalizerFns{ - AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}, - ), - WithBinder(BinderFns{ - BindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return errBoom }, - }), - }, - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "Successful": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - switch o := o.(type) { - case *fake.Claim: - cm := &fake.Claim{} - cm.SetResourceReference(&corev1.ObjectReference{}) - *o = *cm - return nil - case *fake.Managed: - mg := &fake.Managed{} - mg.SetCreationTimestamp(now) - mg.SetBindingPhase(v1alpha1.BindingPhaseBound) - *o = *mg - return nil - default: - return errUnexpected - } - }), - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetResourceReference(&corev1.ObjectReference{}) - want.SetConditions(v1alpha1.Available(), v1alpha1.ReconcileSuccess()) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}, &fake.Class{}, &fake.Managed{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - use: resource.ClassKind(fake.GVK(&fake.Class{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - o: []ReconcilerOption{ - WithClaimFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}), - }, - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - r := NewReconciler(tc.args.m, tc.args.of, tc.args.use, tc.args.with, tc.args.o...) - got, err := r.Reconcile(reconcile.Request{}) - - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("r.Reconcile(...): -want error, +got error:\n%s", diff) - } - - if diff := cmp.Diff(tc.want.result, got); diff != "" { - t.Errorf("r.Reconcile(...): -want, +got:\n%s", diff) - } - }) - } -} diff --git a/pkg/reconciler/claimdefaulting/doc.go b/pkg/reconciler/claimdefaulting/doc.go deleted file mode 100644 index 28a347c..0000000 --- a/pkg/reconciler/claimdefaulting/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -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 claimdefaulting provides a reconciler that sets the default resource -// class for a resource claim. -// Deprecated: See https://github.com/crossplane/crossplane/issues/1670 -package claimdefaulting diff --git a/pkg/reconciler/claimdefaulting/reconciler.go b/pkg/reconciler/claimdefaulting/reconciler.go deleted file mode 100644 index ca3ef28..0000000 --- a/pkg/reconciler/claimdefaulting/reconciler.go +++ /dev/null @@ -1,221 +0,0 @@ -/* -Copyright 2019 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 claimdefaulting - -import ( - "context" - "math/rand" - "strings" - "time" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/event" - "github.com/crossplane/crossplane-runtime/pkg/logging" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" -) - -const ( - claimDefaultingReconcileTimeout = 1 * time.Minute - claimDefaultingReconcileMaxJitterMs = 1500 - - aShortWait = 30 * time.Second -) - -// Error strings. -const ( - errGetClaim = "cannot get resource claim" - errUpdateClaim = "cannot update resource claim" - errListClasses = "cannot list resource classes" -) - -// Event reasons. -const reasonClassFound = "DefaultResourceClass" - -// ControllerName returns the recommended name for controllers that use this -// package to reconcile a particular kind of resource claim. -func ControllerName(kind string) string { - return "claimdefaulting/" + strings.ToLower(kind) -} - -// A Jitterer sleeps for a random amount of time in order to decrease the chance -// of any one controller predictably winning the race to schedule claims to a -// class, for example because it has fewer classes to list and select from than -// its competitors. -type Jitterer func() - -// A Reconciler reconciles resource claims by setting their class reference to -// the resource class annotated as the default. If multiple classes are -// annotated as the default one of the annotated classes will be set, but which -// one is undefined. -type Reconciler struct { - client client.Client - newClaim func() resource.Claim - classKind resource.ClassKind - jitter Jitterer - - log logging.Logger - record event.Recorder -} - -// A ReconcilerOption configures a Reconciler. -type ReconcilerOption func(*Reconciler) - -// WithDefaultingJitterer specifies the Jitterer a Reconciler should use. -func WithDefaultingJitterer(j Jitterer) ReconcilerOption { - return func(r *Reconciler) { - r.jitter = j - } -} - -// WithLogger specifies how the Reconciler should log messages. -func WithLogger(l logging.Logger) ReconcilerOption { - return func(r *Reconciler) { - r.log = l - } -} - -// WithRecorder specifies how the Reconciler should record events. -func WithRecorder(er event.Recorder) ReconcilerOption { - return func(r *Reconciler) { - r.record = er - } -} - -// NewReconciler returns a Reconciler that sets the class reference of a -// resource claim to the resource class annotated as the default. -func NewReconciler(m manager.Manager, of resource.ClaimKind, to resource.ClassKind, o ...ReconcilerOption) *Reconciler { - nc := func() resource.Claim { - return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Claim) - } - - // Panic early if we've been asked to reconcile a claim or resource kind - // that has not been registered with our controller manager's scheme. - _ = nc() - - r := &Reconciler{ - client: m.GetClient(), - newClaim: nc, - classKind: to, - jitter: func() { - random := rand.New(rand.NewSource(time.Now().UnixNano())) - time.Sleep(time.Duration(random.Intn(claimDefaultingReconcileMaxJitterMs)) * time.Millisecond) - }, - log: logging.NewNopLogger(), - record: event.NewNopRecorder(), - } - - for _, ro := range o { - ro(r) - } - - return r -} - -// Reconcile a resource claim by using its class selector to select and allocate -// it a resource class. -func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) { - log := r.log.WithValues("request", req) - log.Debug("Reconciling") - - ctx, cancel := context.WithTimeout(context.Background(), claimDefaultingReconcileTimeout) - defer cancel() - - claim := r.newClaim() - if err := r.client.Get(ctx, req.NamespacedName, claim); err != nil { - // There's no need to requeue if we no longer exist. Otherwise we'll be - // requeued implicitly because we return an error. - log.Debug("Cannot get resource claim", "error", err) - return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetClaim) - } - - record := r.record.WithAnnotations( - "external-name", meta.GetExternalName(claim), - "class-kind", r.classKind.Kind, - ) - log = log.WithValues( - "uid", claim.GetUID(), - "version", claim.GetResourceVersion(), - "external-name", meta.GetExternalName(claim), - "class-kind", r.classKind.Kind, - ) - - // There could be several controllers racing to schedule this claim. If it - // was scheduled since we were queued then another controller won and we - // should abort. - if claim.GetClassReference() != nil { - log.Debug("Resource class is already set") - return reconcile.Result{Requeue: false}, nil - } - - classes := &unstructured.UnstructuredList{} - classes.SetGroupVersionKind(r.classKind.List()) - - if err := r.client.List(ctx, classes); err != nil { - // Claim defaulting controllers don't update the synced status because - // no one scheduler has the full view of whether the process failed or - // succeeded. It's possible another controller can successfully set a - // class even though we can't, so it would be confusing to mark this - // claim as failing to be reconciled. Instead we return an error - we'll - // be requeued but abort immediately if the claim was defaulted. - log.Debug("Cannot list resource classes", "error", err) - return reconcile.Result{}, errors.Wrap(err, errListClasses) - } - - defaults := []unstructured.Unstructured{} - for _, c := range classes.Items { - if c.GetAnnotations()[v1alpha1.AnnotationDefaultClassKey] == v1alpha1.AnnotationDefaultClassValue { - defaults = append(defaults, c) - } - } - - if len(defaults) == 0 { - // None of our classes are annotated as the default. We can't be sure - // whether another controller owns the default class, or whether there - // is no default class, so we requeue after a short wait. We'll abort - // the next reconcile immediately if another controller defaulted the - // claim. - log.Debug("No default resource classes found", "requeue-after", time.Now().Add(aShortWait)) - return reconcile.Result{RequeueAfter: aShortWait}, nil - } - - random := rand.New(rand.NewSource(time.Now().UnixNano())) - selected := defaults[random.Intn(len(defaults))] - claim.SetClassReference(meta.ReferenceTo(&selected, schema.GroupVersionKind(r.classKind))) - - // There could be several controllers racing to default this claim to a - // class. We sleep for a randomly jittered amount of time before trying to - // update the class reference to decrease the chance of any one controller - // predictably winning the race, for example because it has fewer classes to - // list and select from than its competitors. - r.jitter() - - // Attempt to set the class reference. If a competing controller beat us - // we'll fail the write because the claim's resource version has changed - // since we read it. We'll be requeued, but will abort immediately if the - // claim was defaulted. - log.Debug("Attempting to set resource class", "class-name", selected.GetName()) - record.Event(claim, event.Normal(reasonClassFound, "Selected default resource class", "class-name", selected.GetName())) - return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Update(ctx, claim), errUpdateClaim) -} diff --git a/pkg/reconciler/claimdefaulting/reconciler_test.go b/pkg/reconciler/claimdefaulting/reconciler_test.go deleted file mode 100644 index 70ee449..0000000 --- a/pkg/reconciler/claimdefaulting/reconciler_test.go +++ /dev/null @@ -1,271 +0,0 @@ -/* -Copyright 2019 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 claimdefaulting - -import ( - "strconv" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" - "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" -) - -var _ reconcile.Reconciler = &Reconciler{} - -func TestReconciler(t *testing.T) { - name := "coolName" - uid := types.UID("definitely-a-uuid") - - type args struct { - m manager.Manager - of resource.ClaimKind - to resource.ClassKind - } - - type want struct { - result reconcile.Result - err error - } - - errBoom := errors.New("boom") - - cases := map[string]struct { - args args - want want - }{ - "GetClaimError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)}, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errGetClaim)}, - }, - "ClaimNotFound": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, ""))}, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{}}, - }, - "ClaimHasClassRef": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassReferencer: fake.ClassReferencer{Ref: &corev1.ObjectReference{}}} - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "ListClassesError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil), - MockList: test.NewMockListFn(errBoom), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errListClasses)}, - }, - "NoClassesAnnotatedDefault": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(fake.GVK(&fake.Class{})) - u.SetName(name) - u.SetUID(uid) - l := o.(*unstructured.UnstructuredList) - l.Items = []unstructured.Unstructured{*u} - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "UpdateClaimError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(fake.GVK(&fake.Class{})) - u.SetName(name) - u.SetUID(uid) - u.SetAnnotations(map[string]string{v1alpha1.AnnotationDefaultClassKey: v1alpha1.AnnotationDefaultClassValue}) - l := o.(*unstructured.UnstructuredList) - l.Items = []unstructured.Unstructured{*u} - return nil - }), - MockUpdate: test.NewMockUpdateFn(errBoom), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errUpdateClaim)}, - }, - "Successful": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(fake.GVK(&fake.Class{})) - u.SetName(name) - u.SetUID(uid) - u.SetAnnotations(map[string]string{v1alpha1.AnnotationDefaultClassKey: v1alpha1.AnnotationDefaultClassValue}) - l := o.(*unstructured.UnstructuredList) - l.Items = []unstructured.Unstructured{*u} - return nil - }), - MockUpdate: test.NewMockUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetClassReference(&corev1.ObjectReference{ - APIVersion: fake.GVK(&fake.Class{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Class{}).Kind, - Name: name, - UID: uid, - }) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - r := NewReconciler(tc.args.m, tc.args.of, tc.args.to, WithDefaultingJitterer(func() {})) - got, err := r.Reconcile(reconcile.Request{}) - - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("r.Reconcile(...): -want error, +got error:\n%s", diff) - } - - if diff := cmp.Diff(tc.want.result, got); diff != "" { - t.Errorf("r.Reconcile(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestReconcilerRandomness(t *testing.T) { - classes := 10 - reconciles := 100 - refs := make([]*corev1.ObjectReference, 0) - - newClass := func(i int) unstructured.Unstructured { - u := &unstructured.Unstructured{} - u.SetUID(types.UID(strconv.Itoa(i))) - u.SetAnnotations(map[string]string{v1alpha1.AnnotationDefaultClassKey: v1alpha1.AnnotationDefaultClassValue}) - return *u - } - - m := &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - l := o.(*unstructured.UnstructuredList) - for i := 0; i < classes; i++ { - l.Items = append(l.Items, newClass(i)) - } - return nil - }), - MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - ls := obj.(resource.ClassReferencer) - refs = append(refs, ls.GetClassReference()) - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - } - - r := NewReconciler(m, - resource.ClaimKind(fake.GVK(&fake.Claim{})), - resource.ClassKind(fake.GVK(&fake.Class{})), - WithDefaultingJitterer(func() {})) - - for i := 0; i < reconciles; i++ { - r.Reconcile(reconcile.Request{}) - } - - distribution := map[types.UID]int{} - for _, ref := range refs { - distribution[ref.UID]++ - } - - // The goal here is to test whether we're random-ish, i.e. that we're not - // picking the same class every time. - if len(distribution) < 2 { - t.Errorf("want > 1 resource classes selected, got %d", len(distribution)) - } -} diff --git a/pkg/reconciler/claimscheduling/doc.go b/pkg/reconciler/claimscheduling/doc.go deleted file mode 100644 index 892c98a..0000000 --- a/pkg/reconciler/claimscheduling/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -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 claimscheduling provides a reconciler that selects a resource class -// for a resource claim. -// Deprecated: See https://github.com/crossplane/crossplane/issues/1670 -package claimscheduling diff --git a/pkg/reconciler/claimscheduling/reconciler.go b/pkg/reconciler/claimscheduling/reconciler.go deleted file mode 100644 index 33760d1..0000000 --- a/pkg/reconciler/claimscheduling/reconciler.go +++ /dev/null @@ -1,213 +0,0 @@ -/* -Copyright 2019 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 claimscheduling - -import ( - "context" - "math/rand" - "strings" - "time" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/pkg/event" - "github.com/crossplane/crossplane-runtime/pkg/logging" - "github.com/crossplane/crossplane-runtime/pkg/meta" - "github.com/crossplane/crossplane-runtime/pkg/resource" -) - -const ( - claimSchedulingReconcileTimeout = 1 * time.Minute - claimSchedulingReconcileMaxJitterMs = 1500 - - aShortWait = 30 * time.Second -) - -// Error strings. -const ( - errGetClaim = "cannot get resource claim" - errUpdateClaim = "cannot update resource claim" - errListClasses = "cannot list resource classes" -) - -// Event reasons. -const reasonClassFound = "SelectedResourceClass" - -// ControllerName returns the recommended name for controllers that use this -// package to reconcile a particular kind of resource claim. -func ControllerName(kind string) string { - return "claimscheduling/" + strings.ToLower(kind) -} - -// A Jitterer sleeps for a random amount of time in order to decrease the chance -// of any one controller predictably winning the race to schedule claims to a -// class, for example because it has fewer classes to list and select from than -// its competitors. -type Jitterer func() - -// A Reconciler schedules resource claims to a resource class that matches their -// class selector. Claims are reconciled by randomly selecting a matching -// resource class and attempting to set it as the claim's class reference. The -// Reconciler is designed for use in claim scheduling controllers that race -// several others to schedule a claim. -type Reconciler struct { - client client.Client - newClaim func() resource.Claim - classKind resource.ClassKind - jitter Jitterer - - log logging.Logger - record event.Recorder -} - -// A ReconcilerOption configures a Reconciler. -type ReconcilerOption func(*Reconciler) - -// WithSchedulingJitterer specifies the Jitterer a Reconciler should use. -func WithSchedulingJitterer(j Jitterer) ReconcilerOption { - return func(r *Reconciler) { - r.jitter = j - } -} - -// WithLogger specifies how the Reconciler should log messages. -func WithLogger(l logging.Logger) ReconcilerOption { - return func(r *Reconciler) { - r.log = l - } -} - -// WithRecorder specifies how the Reconciler should record events. -func WithRecorder(er event.Recorder) ReconcilerOption { - return func(r *Reconciler) { - r.record = er - } -} - -// NewReconciler returns a Reconciler that schedules resource claims to a -// resource class that matches their class selector. -func NewReconciler(m manager.Manager, of resource.ClaimKind, to resource.ClassKind, o ...ReconcilerOption) *Reconciler { - nc := func() resource.Claim { - return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Claim) - } - - // Panic early if we've been asked to reconcile a claim or resource kind - // that has not been registered with our controller manager's scheme. - _ = nc() - - r := &Reconciler{ - client: m.GetClient(), - newClaim: nc, - classKind: to, - jitter: func() { - random := rand.New(rand.NewSource(time.Now().UnixNano())) - time.Sleep(time.Duration(random.Intn(claimSchedulingReconcileMaxJitterMs)) * time.Millisecond) - }, - log: logging.NewNopLogger(), - record: event.NewNopRecorder(), - } - - for _, ro := range o { - ro(r) - } - - return r -} - -// Reconcile a resource claim by using its class selector to select and allocate -// it a resource class. -func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) { - log := r.log.WithValues("request", req) - log.Debug("Reconciling") - - ctx, cancel := context.WithTimeout(context.Background(), claimSchedulingReconcileTimeout) - defer cancel() - - claim := r.newClaim() - if err := r.client.Get(ctx, req.NamespacedName, claim); err != nil { - // There's no need to requeue if we no longer exist. Otherwise we'll be - // requeued implicitly because we return an error. - log.Debug("Cannot get resource claim", "error", err) - return reconcile.Result{}, errors.Wrap(resource.IgnoreNotFound(err), errGetClaim) - } - - record := r.record.WithAnnotations( - "external-name", meta.GetExternalName(claim), - "class-kind", r.classKind.Kind, - ) - log = log.WithValues( - "uid", claim.GetUID(), - "version", claim.GetResourceVersion(), - "external-name", meta.GetExternalName(claim), - "class-kind", r.classKind.Kind, - ) - - // There could be several controllers racing to schedule this claim. If it - // was scheduled since we were queued then another controller won and we - // should abort. - if claim.GetClassReference() != nil { - log.Debug("Resource class is already set") - return reconcile.Result{Requeue: false}, nil - } - - classes := &unstructured.UnstructuredList{} - classes.SetGroupVersionKind(r.classKind.List()) - - if err := r.client.List(ctx, classes, client.MatchingLabels(claim.GetClassSelector().MatchLabels)); err != nil { - // Claim scheduler controllers don't update the synced status because - // no one scheduler has the full view of whether the process failed or - // succeeded. It's possible another controller can successfully set a - // class even though we can't, so it would be confusing to mark this - // claim as failing to be reconciled. Instead we return an error - we'll - // be requeued but abort immediately if the claim was scheduled. - log.Debug("Cannot list resource classes", "error", err) - return reconcile.Result{}, errors.Wrap(err, errListClasses) - } - - if len(classes.Items) == 0 { - // None of our classes matched the selector. We can't be sure whether - // another controller owns classes that matched the selector, or whether - // no classes match, so we requeue after a short wait. We'll abort the - // next reconcile immediately if another controller scheduled the claim. - log.Debug("No matching resource classes found", "requeue-after", time.Now().Add(aShortWait)) - return reconcile.Result{RequeueAfter: aShortWait}, nil - } - - random := rand.New(rand.NewSource(time.Now().UnixNano())) - selected := classes.Items[random.Intn(len(classes.Items))] - claim.SetClassReference(meta.ReferenceTo(&selected, schema.GroupVersionKind(r.classKind))) - - // There could be several controllers racing to schedule this claim to a - // class. We sleep for a randomly jittered amount of time before trying to - // update the class reference to decrease the chance of any one controller - // predictably winning the race, for example because it has fewer classes to - // list and select from than its competitors. - r.jitter() - - // Attempt to set the class reference. If a competing controller beat us - // we'll fail the write because the claim's resource version has changed - // since we read it. We'll be requeued, but will abort immediately if the - // claim was scheduled. - log.Debug("Attempting to set resource class", "class-name", selected.GetName()) - record.Event(claim, event.Normal(reasonClassFound, "Selected matching resource class", "class-name", selected.GetName())) - return reconcile.Result{Requeue: false}, errors.Wrap(r.client.Update(ctx, claim), errUpdateClaim) -} diff --git a/pkg/reconciler/claimscheduling/reconciler_test.go b/pkg/reconciler/claimscheduling/reconciler_test.go deleted file mode 100644 index 2d9441b..0000000 --- a/pkg/reconciler/claimscheduling/reconciler_test.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright 2019 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 claimscheduling - -import ( - "strconv" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/crossplane/crossplane-runtime/pkg/resource" - "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" -) - -var _ reconcile.Reconciler = &Reconciler{} - -func TestReconciler(t *testing.T) { - name := "coolName" - uid := types.UID("definitely-a-uuid") - - type args struct { - m manager.Manager - of resource.ClaimKind - to resource.ClassKind - } - - type want struct { - result reconcile.Result - err error - } - - errBoom := errors.New("boom") - - cases := map[string]struct { - args args - want want - }{ - "GetClaimError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{MockGet: test.NewMockGetFn(errBoom)}, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errGetClaim)}, - }, - "ClaimNotFound": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{MockGet: test.NewMockGetFn(kerrors.NewNotFound(schema.GroupResource{}, ""))}, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{}}, - }, - "ClaimHasClassRef": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassReferencer: fake.ClassReferencer{Ref: &corev1.ObjectReference{}}} - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - "ListClassesError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(errBoom), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errListClasses)}, - }, - "NoClassesMatchLabels": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(nil), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{RequeueAfter: aShortWait}}, - }, - "UpdateClaimError": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(fake.GVK(&fake.Class{})) - u.SetName(name) - u.SetUID(uid) - l := o.(*unstructured.UnstructuredList) - l.Items = []unstructured.Unstructured{*u} - return nil - }), - MockUpdate: test.NewMockUpdateFn(errBoom), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{err: errors.Wrap(errBoom, errUpdateClaim)}, - }, - "Successful": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - u := &unstructured.Unstructured{} - u.SetGroupVersionKind(fake.GVK(&fake.Class{})) - u.SetName(name) - u.SetUID(uid) - l := o.(*unstructured.UnstructuredList) - l.Items = []unstructured.Unstructured{*u} - return nil - }), - MockUpdate: test.NewMockUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Claim{} - want.SetClassSelector(&metav1.LabelSelector{}) - want.SetClassReference(&corev1.ObjectReference{ - APIVersion: fake.GVK(&fake.Class{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Class{}).Kind, - Name: name, - UID: uid, - }) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - }, - of: resource.ClaimKind(fake.GVK(&fake.Claim{})), - to: resource.ClassKind(fake.GVK(&fake.Class{})), - }, - want: want{result: reconcile.Result{Requeue: false}}, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - r := NewReconciler(tc.args.m, tc.args.of, tc.args.to, WithSchedulingJitterer(func() {})) - got, err := r.Reconcile(reconcile.Request{}) - - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("r.Reconcile(...): -want error, +got error:\n%s", diff) - } - - if diff := cmp.Diff(tc.want.result, got); diff != "" { - t.Errorf("r.Reconcile(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestReconcilerRandomness(t *testing.T) { - classes := 10 - reconciles := 100 - refs := make([]*corev1.ObjectReference, 0) - - newClass := func(i int) unstructured.Unstructured { - u := &unstructured.Unstructured{} - u.SetUID(types.UID(strconv.Itoa(i))) - return *u - } - - m := &fake.Manager{ - Client: &test.MockClient{ - MockGet: test.NewMockGetFn(nil, func(o runtime.Object) error { - c := o.(*fake.Claim) - *c = fake.Claim{ClassSelector: fake.ClassSelector{Sel: &metav1.LabelSelector{}}} - return nil - }), - MockList: test.NewMockListFn(nil, func(o runtime.Object) error { - l := o.(*unstructured.UnstructuredList) - for i := 0; i < classes; i++ { - l.Items = append(l.Items, newClass(i)) - } - return nil - }), - MockUpdate: test.NewMockUpdateFn(nil, func(obj runtime.Object) error { - ls := obj.(resource.ClassReferencer) - refs = append(refs, ls.GetClassReference()) - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Claim{}), - } - - r := NewReconciler(m, - resource.ClaimKind(fake.GVK(&fake.Claim{})), - resource.ClassKind(fake.GVK(&fake.Class{})), - WithSchedulingJitterer(func() {})) - - for i := 0; i < reconciles; i++ { - r.Reconcile(reconcile.Request{}) - } - - distribution := map[types.UID]int{} - for _, ref := range refs { - distribution[ref.UID]++ - } - - // The goal here is to test whether we're random-ish, i.e. that we're not - // picking the same class every time. - if len(distribution) < 2 { - t.Errorf("want > 1 resource classes selected, got %d", len(distribution)) - } -} diff --git a/pkg/reconciler/managed/reconciler.go b/pkg/reconciler/managed/reconciler.go index 255da20..b1838db 100644 --- a/pkg/reconciler/managed/reconciler.go +++ b/pkg/reconciler/managed/reconciler.go @@ -545,7 +545,7 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) if meta.WasDeleted(managed) { log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp()) - if observation.ResourceExists && shouldDelete(managed) { + if observation.ResourceExists && managed.GetDeletionPolicy() != v1alpha1.DeletionOrphan { if err := external.Delete(externalCtx, managed); err != nil { // We'll hit this condition if we can't delete our external // resource, for example if our provider credentials don't have @@ -695,23 +695,3 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) managed.SetConditions(v1alpha1.ReconcileSuccess()) return reconcile.Result{RequeueAfter: r.longWait}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) } - -// TODO(negz): ReclaimPolicy will be deprecated alongside resource claims; this -// should be inlined when claims (and thus reclaim policies) are removed. -func shouldDelete(mg resource.Managed) bool { - switch { - // The deletion policy should take precedence over the reclaim policy. - case mg.GetDeletionPolicy() == v1alpha1.DeletionOrphan: - return false - case mg.GetDeletionPolicy() == v1alpha1.DeletionDelete: - return true - - case mg.GetReclaimPolicy() == v1alpha1.ReclaimRetain: - return false - case mg.GetReclaimPolicy() == v1alpha1.ReclaimDelete: - return true - } - - // If no policy is set, we default to deleting the resource. - return true -} diff --git a/pkg/reconciler/managed/reconciler_test.go b/pkg/reconciler/managed/reconciler_test.go index e03f718..5dfbeaf 100644 --- a/pkg/reconciler/managed/reconciler_test.go +++ b/pkg/reconciler/managed/reconciler_test.go @@ -207,13 +207,13 @@ func TestReconciler(t *testing.T) { MockGet: test.NewMockGetFn(nil, func(obj runtime.Object) error { mg := obj.(*fake.Managed) mg.SetDeletionTimestamp(&now) - mg.SetReclaimPolicy(v1alpha1.ReclaimDelete) + mg.SetDeletionPolicy(v1alpha1.DeletionDelete) return nil }), MockStatusUpdate: test.MockStatusUpdateFn(func(_ context.Context, obj runtime.Object, _ ...client.UpdateOption) error { want := &fake.Managed{} want.SetDeletionTimestamp(&now) - want.SetReclaimPolicy(v1alpha1.ReclaimDelete) + want.SetDeletionPolicy(v1alpha1.DeletionDelete) want.SetConditions(v1alpha1.ReconcileError(errors.Wrap(errBoom, errReconcileDelete))) if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" { reason := "An error deleting an external resource should be reported as a conditioned status." @@ -251,13 +251,13 @@ func TestReconciler(t *testing.T) { MockGet: test.NewMockGetFn(nil, func(obj runtime.Object) error { mg := obj.(*fake.Managed) mg.SetDeletionTimestamp(&now) - mg.SetReclaimPolicy(v1alpha1.ReclaimDelete) + mg.SetDeletionPolicy(v1alpha1.DeletionDelete) return nil }), MockStatusUpdate: test.MockStatusUpdateFn(func(_ context.Context, obj runtime.Object, _ ...client.UpdateOption) error { want := &fake.Managed{} want.SetDeletionTimestamp(&now) - want.SetReclaimPolicy(v1alpha1.ReclaimDelete) + want.SetDeletionPolicy(v1alpha1.DeletionDelete) want.SetConditions(v1alpha1.ReconcileSuccess()) if diff := cmp.Diff(want, obj, test.EquateConditions()); diff != "" { reason := "A deleted external resource should be reported as a conditioned status." @@ -759,58 +759,3 @@ func TestReconciler(t *testing.T) { }) } } - -func TestShouldDelete(t *testing.T) { - cases := map[string]struct { - reason string - mg resource.Managed - want bool - }{ - "DeletionPolicyDelete": { - reason: "The delete deletion policy should take precedence over the reclaim policy.", - mg: &fake.Managed{ - Orphanable: fake.Orphanable{Policy: v1alpha1.DeletionDelete}, - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - }, - want: true, - }, - "DeletionPolicyOrphan": { - reason: "The orphan deletion policy should take precedence over the reclaim policy.", - mg: &fake.Managed{ - Orphanable: fake.Orphanable{Policy: v1alpha1.DeletionOrphan}, - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - }, - want: false, - }, - "ReclaimPolicyDelete": { - reason: "The delete reclaim policy should take effect when no deletion policy exists.", - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimDelete}, - }, - want: true, - }, - "ReclaimPolicyRetain": { - reason: "The retain reclaim policy should take effect when no deletion policy exists.", - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{Policy: v1alpha1.ReclaimRetain}, - }, - want: false, - }, - "NoPolicy": { - reason: "Resources should be deleted when no deletion or reclaim policy is specified.", - mg: &fake.Managed{ - Reclaimer: fake.Reclaimer{}, - }, - want: true, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := shouldDelete(tc.mg) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("\nReason: %s\nshouldDelete(...): -want, +got:\n%s", tc.reason, diff) - } - }) - } -} diff --git a/pkg/reconciler/secret/reconciler_test.go b/pkg/reconciler/secret/reconciler_test.go index c098509..d8edd7b 100644 --- a/pkg/reconciler/secret/reconciler_test.go +++ b/pkg/reconciler/secret/reconciler_test.go @@ -55,14 +55,14 @@ func TestReconciler(t *testing.T) { Name: "coolmanagedsecret", }}, } - cm := &fake.Claim{ - ObjectMeta: metav1.ObjectMeta{Namespace: "coolns", Name: "coolclaim"}, + tg := &fake.Target{ + ObjectMeta: metav1.ObjectMeta{Namespace: "coolns", Name: "cooltarget"}, LocalConnectionSecretWriterTo: fake.LocalConnectionSecretWriterTo{Ref: &v1alpha1.LocalSecretReference{ - Name: "coolclaimsecret", + Name: "cooltargetsecret", }}, } from := resource.ConnectionSecretFor(mg, fake.GVK(mg)) - to := resource.LocalConnectionSecretFor(cm, fake.GVK(cm)) + to := resource.LocalConnectionSecretFor(tg, fake.GVK(tg)) fromData := map[string][]byte{"cool": {1}} diff --git a/pkg/reconciler/target/reconciler.go b/pkg/reconciler/target/reconciler.go index f9ff6d8..7f0b6bd 100644 --- a/pkg/reconciler/target/reconciler.go +++ b/pkg/reconciler/target/reconciler.go @@ -43,15 +43,13 @@ const ( // Error strings const ( - errGetTarget = "unable to get Target" - errManagedResourceIsNotBound = "managed resource in Target clusterRef is unbound" - errUpdateTarget = "unable to update Target" + errGetTarget = "unable to get Target" + errUpdateTarget = "unable to update Target" ) // Event reasons. const ( reasonSetSecretRef event.Reason = "SetSecretRef" - reasonWaitingUntilBound event.Reason = "WaitingUntilBound" reasonCannotGetManaged event.Reason = "CannotGetManaged" reasonCannotPropagateSecret event.Reason = "CannotPropagateSecret" reasonPropagatedSecret event.Reason = "PropagatedSecret" @@ -201,14 +199,6 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, target), errUpdateTarget) } - if !resource.IsBound(managed) { - // TODO(negz): Should we really consider this an error? - log.Debug("Managed resource is not yet bound to a resource claim", "requeue-after", time.Now().Add(aShortWait)) - record.Event(target, event.Normal(reasonWaitingUntilBound, "Managed resource is not yet bound to a resource claim")) - target.SetConditions(SecretPropagationError(errors.New(errManagedResourceIsNotBound))) - return reconcile.Result{RequeueAfter: aShortWait}, errors.Wrap(r.client.Status().Update(ctx, target), errUpdateTarget) - } - if err := r.propagator.PropagateConnection(ctx, target, managed); err != nil { // If we fail to propagate the connection secret of a bound managed // resource, we try again after a short wait. diff --git a/pkg/reconciler/target/reconciler_test.go b/pkg/reconciler/target/reconciler_test.go index 93599e3..7e68d48 100644 --- a/pkg/reconciler/target/reconciler_test.go +++ b/pkg/reconciler/target/reconciler_test.go @@ -248,67 +248,6 @@ func TestReconciler(t *testing.T) { result: reconcile.Result{RequeueAfter: aShortWait}, }, }, - "ErrorManagedNotBound": { - args: args{ - m: &fake.Manager{ - Client: &test.MockClient{ - MockGet: func(_ context.Context, n types.NamespacedName, o runtime.Object) error { - switch o := o.(type) { - case *fake.Target: - tg := &fake.Target{ObjectMeta: metav1.ObjectMeta{ - UID: tguid, - Name: tgname, - Namespace: ns, - }} - tg.SetResourceReference(&corev1.ObjectReference{ - Name: mgname, - }) - tg.SetWriteConnectionSecretToReference(&v1alpha1.LocalSecretReference{ - Name: tgcsname, - }) - *o = *tg - return nil - case *fake.Managed: - mg := &fake.Managed{ObjectMeta: metav1.ObjectMeta{ - UID: mguid, - Name: mgname, - }} - mg.SetWriteConnectionSecretToReference(&v1alpha1.SecretReference{ - Name: mgcsname, - Namespace: mgcsnamespace, - }) - mg.SetBindingPhase(v1alpha1.BindingPhaseUnbound) - *o = *mg - return nil - default: - return errUnexpected - } - }, - MockStatusUpdate: test.NewMockStatusUpdateFn(nil, func(got runtime.Object) error { - want := &fake.Target{} - want.SetName(tgname) - want.SetNamespace(ns) - want.SetUID(tguid) - want.SetResourceReference(&corev1.ObjectReference{ - Name: mgname, - }) - want.SetWriteConnectionSecretToReference(&v1alpha1.LocalSecretReference{Name: tgcsname}) - want.SetConditions(SecretPropagationError(errors.New(errManagedResourceIsNotBound))) - if diff := cmp.Diff(want, got, test.EquateConditions()); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - return nil - }), - }, - Scheme: fake.SchemeWith(&fake.Target{}, &fake.Managed{}), - }, - of: resource.TargetKind(fake.GVK(&fake.Target{})), - with: resource.ManagedKind(fake.GVK(&fake.Managed{})), - }, - want: want{ - result: reconcile.Result{RequeueAfter: aShortWait}, - }, - }, "ErrorSecretPropagationFailed": { args: args{ m: &fake.Manager{ @@ -338,7 +277,6 @@ func TestReconciler(t *testing.T) { Name: mgcsname, Namespace: mgcsnamespace, }) - mg.SetBindingPhase(v1alpha1.BindingPhaseBound) *o = *mg return nil default: @@ -406,7 +344,6 @@ func TestReconciler(t *testing.T) { Name: mgcsname, Namespace: mgcsnamespace, }) - mg.SetBindingPhase(v1alpha1.BindingPhaseBound) *o = *mg return nil default: diff --git a/pkg/resource/api_test.go b/pkg/resource/api_test.go index c4c8d05..3899b58 100644 --- a/pkg/resource/api_test.go +++ b/pkg/resource/api_test.go @@ -56,7 +56,7 @@ func TestPropagateConnection(t *testing.T) { }, } - cm := &fake.Claim{ + cm := &fake.CompositeClaim{ ObjectMeta: metav1.ObjectMeta{Namespace: cmcsns}, LocalConnectionSecretWriterTo: fake.LocalConnectionSecretWriterTo{ Ref: &v1alpha1.LocalSecretReference{Name: cmcsname}, @@ -83,7 +83,7 @@ func TestPropagateConnection(t *testing.T) { "ClaimDoesNotWantConnectionSecret": { reason: "The managed resource's secret should not be propagated if the claim does not want to write one", args: args{ - o: &fake.Claim{}, + o: &fake.CompositeClaim{}, mg: mg, }, want: nil, diff --git a/pkg/resource/enqueue_handlers.go b/pkg/resource/enqueue_handlers.go index e6c388e..8e7837c 100644 --- a/pkg/resource/enqueue_handlers.go +++ b/pkg/resource/enqueue_handlers.go @@ -30,41 +30,6 @@ type adder interface { Add(item interface{}) } -// EnqueueRequestForClaim enqueues a reconcile.Request for the NamespacedName -// of a ClaimReferencer's ClaimReference. -type EnqueueRequestForClaim struct{} - -// Create adds a NamespacedName for the supplied CreateEvent if its Object is a -// ClaimReferencer. -func (e *EnqueueRequestForClaim) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) { - addClaim(evt.Object, q) -} - -// Update adds a NamespacedName for the supplied UpdateEvent if its Objects are -// ClaimReferencers. -func (e *EnqueueRequestForClaim) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) { - addClaim(evt.ObjectOld, q) - addClaim(evt.ObjectNew, q) -} - -// Delete adds a NamespacedName for the supplied DeleteEvent if its Object is a -// ClaimReferencer. -func (e *EnqueueRequestForClaim) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) { - addClaim(evt.Object, q) -} - -// Generic adds a NamespacedName for the supplied GenericEvent if its Object is a -// ClaimReferencer. -func (e *EnqueueRequestForClaim) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) { - addClaim(evt.Object, q) -} - -func addClaim(obj runtime.Object, queue adder) { - if cr, ok := obj.(ClaimReferencer); ok && cr.GetClaimReference() != nil { - queue.Add(reconcile.Request{NamespacedName: meta.NamespacedNameOf(cr.GetClaimReference())}) - } -} - // EnqueueRequestForPropagated enqueues a reconcile.Request for the // NamespacedName of a propagated object, i.e. an object with propagation // metadata annotations. diff --git a/pkg/resource/enqueue_handlers_test.go b/pkg/resource/enqueue_handlers_test.go index b3d0a99..4489c75 100644 --- a/pkg/resource/enqueue_handlers_test.go +++ b/pkg/resource/enqueue_handlers_test.go @@ -20,7 +20,6 @@ import ( "testing" "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" "k8s.io/apimachinery/pkg/types" @@ -32,7 +31,7 @@ import ( ) var ( - _ handler.EventHandler = &EnqueueRequestForClaim{} + _ handler.EventHandler = &EnqueueRequestForPropagated{} ) type addFn func(item interface{}) @@ -41,40 +40,6 @@ func (fn addFn) Add(item interface{}) { fn(item) } -func TestAddClaim(t *testing.T) { - ns := "coolns" - name := "coolname" - - cases := map[string]struct { - obj runtime.Object - queue adder - }{ - "ObjectIsNotAClaimReferencer": { - queue: addFn(func(_ interface{}) { t.Errorf("queue.Add() called unexpectedly") }), - }, - "ObjectHasNilClaimReference": { - obj: &fake.Managed{}, - queue: addFn(func(_ interface{}) { t.Errorf("queue.Add() called unexpectedly") }), - }, - "ObjectHasClaimReference": { - obj: &fake.Managed{ClaimReferencer: fake.ClaimReferencer{Ref: &corev1.ObjectReference{ - Namespace: ns, - Name: name, - }}}, - queue: addFn(func(got interface{}) { - want := reconcile.Request{NamespacedName: types.NamespacedName{Namespace: ns, Name: name}} - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("-want, +got:\n%s", diff) - } - }), - }, - } - - for _, tc := range cases { - addClaim(tc.obj, tc.queue) - } -} - func TestAddPropagated(t *testing.T) { ns := "coolns" name := "coolname" @@ -94,11 +59,11 @@ func TestAddPropagated(t *testing.T) { }, "IsPropagator": { obj: func() runtime.Object { - cm := &fake.Claim{} - cm.SetNamespace(ns) - cm.SetName(name) + tg := &fake.Target{} + tg.SetNamespace(ns) + tg.SetName(name) mg := &fake.Managed{} - meta.AllowPropagation(mg, cm) + meta.AllowPropagation(mg, tg) return mg }(), queue: addFn(func(got interface{}) { diff --git a/pkg/resource/fake/mocks.go b/pkg/resource/fake/mocks.go index 809dbb1..4843d73 100644 --- a/pkg/resource/fake/mocks.go +++ b/pkg/resource/fake/mocks.go @@ -33,15 +33,6 @@ import ( "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" ) -// Bindable is a mock that implements Bindable interface. -type Bindable struct{ Phase v1alpha1.BindingPhase } - -// SetBindingPhase sets the BindingPhase. -func (m *Bindable) SetBindingPhase(p v1alpha1.BindingPhase) { m.Phase = p } - -// GetBindingPhase sets the BindingPhase. -func (m *Bindable) GetBindingPhase() v1alpha1.BindingPhase { return m.Phase } - // Conditioned is a mock that implements Conditioned interface. type Conditioned struct{ Conditions []v1alpha1.Condition } @@ -62,24 +53,6 @@ func (m *ClaimReferencer) SetClaimReference(r *corev1.ObjectReference) { m.Ref = // GetClaimReference gets the ClaimReference. func (m *ClaimReferencer) GetClaimReference() *corev1.ObjectReference { return m.Ref } -// ClassSelector is a mock that implements ClassSelector interface. -type ClassSelector struct{ Sel *metav1.LabelSelector } - -// SetClassSelector sets the ClassSelector. -func (m *ClassSelector) SetClassSelector(s *metav1.LabelSelector) { m.Sel = s } - -// GetClassSelector gets the ClassSelector. -func (m *ClassSelector) GetClassSelector() *metav1.LabelSelector { return m.Sel } - -// ClassReferencer is a mock that implements ClassReferencer interface. -type ClassReferencer struct{ Ref *corev1.ObjectReference } - -// SetClassReference sets the ClassReference. -func (m *ClassReferencer) SetClassReference(r *corev1.ObjectReference) { m.Ref = r } - -// GetClassReference gets the ClassReference. -func (m *ClassReferencer) GetClassReference() *corev1.ObjectReference { return m.Ref } - // ManagedResourceReferencer is a mock that implements ManagedResourceReferencer interface. type ManagedResourceReferencer struct{ Ref *corev1.ObjectReference } @@ -135,15 +108,6 @@ func (m *ConnectionSecretWriterTo) GetWriteConnectionSecretToReference() *v1alph return m.Ref } -// Reclaimer is a mock that implements Reclaimer interface. -type Reclaimer struct{ Policy v1alpha1.ReclaimPolicy } - -// SetReclaimPolicy sets the ReclaimPolicy. -func (m *Reclaimer) SetReclaimPolicy(p v1alpha1.ReclaimPolicy) { m.Policy = p } - -// GetReclaimPolicy gets the ReclaimPolicy. -func (m *Reclaimer) GetReclaimPolicy() v1alpha1.ReclaimPolicy { return m.Policy } - // Orphanable implements the Orphanable interface. type Orphanable struct{ Policy v1alpha1.DeletionPolicy } @@ -225,67 +189,14 @@ func (o *Object) DeepCopyObject() runtime.Object { return out } -// Claim is a mock that implements Claim interface. -type Claim struct { - metav1.ObjectMeta - ClassSelector - ClassReferencer - ManagedResourceReferencer - LocalConnectionSecretWriterTo - v1alpha1.ConditionedStatus - v1alpha1.BindingStatus -} - -// GetObjectKind returns schema.ObjectKind. -func (m *Claim) GetObjectKind() schema.ObjectKind { - return schema.EmptyObjectKind -} - -// DeepCopyObject returns a copy of the object as runtime.Object -func (m *Claim) DeepCopyObject() runtime.Object { - out := &Claim{} - j, err := json.Marshal(m) - if err != nil { - panic(err) - } - _ = json.Unmarshal(j, out) - return out -} - -// Class is a mock that implements Class interface. -type Class struct { - metav1.ObjectMeta - Reclaimer -} - -// GetObjectKind returns schema.ObjectKind. -func (m *Class) GetObjectKind() schema.ObjectKind { - return schema.EmptyObjectKind -} - -// DeepCopyObject returns a copy of the object as runtime.Object -func (m *Class) DeepCopyObject() runtime.Object { - out := &Class{} - j, err := json.Marshal(m) - if err != nil { - panic(err) - } - _ = json.Unmarshal(j, out) - return out -} - // Managed is a mock that implements Managed interface. type Managed struct { metav1.ObjectMeta - ClassReferencer - ClaimReferencer ProviderReferencer ProviderConfigReferencer ConnectionSecretWriterTo Orphanable - Reclaimer v1alpha1.ConditionedStatus - v1alpha1.BindingStatus } // GetObjectKind returns schema.ObjectKind. @@ -357,7 +268,6 @@ type Composite struct { CompositionReferencer ComposedResourcesReferencer ClaimReferencer - Reclaimer ConnectionSecretWriterTo v1alpha1.ConditionedStatus } diff --git a/pkg/resource/interfaces.go b/pkg/resource/interfaces.go index bf879d1..0c33406 100644 --- a/pkg/resource/interfaces.go +++ b/pkg/resource/interfaces.go @@ -26,13 +26,6 @@ import ( "github.com/crossplane/crossplane-runtime/apis/core/v1alpha1" ) -// A Bindable resource may be bound to another resource. Resources are bindable -// when they available for use. -type Bindable interface { - SetBindingPhase(p v1alpha1.BindingPhase) - GetBindingPhase() v1alpha1.BindingPhase -} - // A Conditioned may have conditions set or retrieved. Conditions are typically // indicate the status of both a resource and its reconciliation process. type Conditioned interface { @@ -46,18 +39,6 @@ type ClaimReferencer interface { GetClaimReference() *corev1.ObjectReference } -// A ClassSelector may reference a resource class. -type ClassSelector interface { - SetClassSelector(s *metav1.LabelSelector) - GetClassSelector() *metav1.LabelSelector -} - -// A ClassReferencer may reference a resource class. -type ClassReferencer interface { - SetClassReference(r *corev1.ObjectReference) - GetClassReference() *corev1.ObjectReference -} - // A ManagedResourceReferencer may reference a concrete managed resource. type ManagedResourceReferencer interface { SetResourceReference(r *corev1.ObjectReference) @@ -78,12 +59,6 @@ type ConnectionSecretWriterTo interface { GetWriteConnectionSecretToReference() *v1alpha1.SecretReference } -// A Reclaimer may specify a ReclaimPolicy. -type Reclaimer interface { - SetReclaimPolicy(p v1alpha1.ReclaimPolicy) - GetReclaimPolicy() v1alpha1.ReclaimPolicy -} - // An Orphanable resource may specify a DeletionPolicy. type Orphanable interface { SetDeletionPolicy(p v1alpha1.DeletionPolicy) @@ -145,60 +120,17 @@ type Object interface { runtime.Object } -// A Claim is a Kubernetes object representing an abstract resource claim (e.g. -// an SQL database) that may be bound to a concrete managed resource (e.g. a -// CloudSQL instance). -type Claim interface { - Object - - ClassSelector - ClassReferencer - ManagedResourceReferencer - LocalConnectionSecretWriterTo - - Conditioned - Bindable -} - -// A ClaimList is a list of resource claims. -type ClaimList interface { - runtime.Object - - // GetItems returns the list of resource claims. - GetItems() []Claim -} - -// A Class is a Kubernetes object representing configuration specifications for -// a managed resource. -type Class interface { - Object - - Reclaimer -} - -// A ClassList is a list of resource classes. -type ClassList interface { - runtime.Object - - // GetItems returns the list of resource classes. - GetItems() []Class -} - // A Managed is a Kubernetes object representing a concrete managed // resource (e.g. a CloudSQL instance). type Managed interface { Object - ClassReferencer - ClaimReferencer ProviderReferencer ProviderConfigReferencer ConnectionSecretWriterTo Orphanable - Reclaimer Conditioned - Bindable } // A ManagedList is a list of managed resources. diff --git a/pkg/resource/predicates.go b/pkg/resource/predicates.go index 3959d51..c544a87 100644 --- a/pkg/resource/predicates.go +++ b/pkg/resource/predicates.go @@ -81,23 +81,6 @@ func HasManagedResourceReferenceKind(k ManagedKind) PredicateFn { } } -// HasClassReferenceKind accepts objects that reference the supplied resource -// class kind. -func HasClassReferenceKind(k ClassKind) PredicateFn { - return func(obj runtime.Object) bool { - r, ok := obj.(ClassReferencer) - if !ok { - return false - } - - if r.GetClassReference() == nil { - return false - } - - return r.GetClassReference().GroupVersionKind() == schema.GroupVersionKind(k) - } -} - // IsManagedKind accepts objects that are of the supplied managed resource kind. func IsManagedKind(k ManagedKind, ot runtime.ObjectTyper) PredicateFn { return func(obj runtime.Object) bool { @@ -152,51 +135,3 @@ func IsPropagated() PredicateFn { return nn.Namespace != "" && nn.Name != "" } } - -// HasClassSelector accepts resource claims that do not specify a resource -// class selector. -func HasClassSelector() PredicateFn { - return func(obj runtime.Object) bool { - cs, ok := obj.(ClassSelector) - if !ok { - return false - } - return cs.GetClassSelector() != nil - } -} - -// HasNoClassSelector accepts resource claims that do not specify a resource -// class selector. -func HasNoClassSelector() PredicateFn { - return func(obj runtime.Object) bool { - cs, ok := obj.(ClassSelector) - if !ok { - return false - } - return cs.GetClassSelector() == nil - } -} - -// HasNoClassReference accepts resource claims that do not reference a specific -// resource class. -func HasNoClassReference() PredicateFn { - return func(obj runtime.Object) bool { - cr, ok := obj.(ClassReferencer) - if !ok { - return false - } - return cr.GetClassReference() == nil - } -} - -// HasNoManagedResourceReference accepts resource claims that do not reference a -// specific managed resource. -func HasNoManagedResourceReference() PredicateFn { - return func(obj runtime.Object) bool { - cr, ok := obj.(ManagedResourceReferencer) - if !ok { - return false - } - return cr.GetResourceReference() == nil - } -} diff --git a/pkg/resource/predicates_test.go b/pkg/resource/predicates_test.go index 0e4cf17..bedd426 100644 --- a/pkg/resource/predicates_test.go +++ b/pkg/resource/predicates_test.go @@ -25,11 +25,9 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/crossplane/crossplane-runtime/pkg/meta" "github.com/crossplane/crossplane-runtime/pkg/resource/fake" - "github.com/crossplane/crossplane-runtime/pkg/test" ) func TestAnyOf(t *testing.T) { @@ -102,88 +100,6 @@ func TestAllOf(t *testing.T) { } } -func TestHasManagedResourceReferenceKind(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - c client.Client - kind ManagedKind - want bool - }{ - "NotAClassReferencer": { - c: &test.MockClient{}, - kind: ManagedKind(fake.GVK(&fake.Managed{})), - want: false, - }, - "HasNoResourceReference": { - obj: &fake.Claim{}, - kind: ManagedKind(fake.GVK(&fake.Managed{})), - want: false, - }, - "HasCorrectResourceReference": { - obj: &fake.Claim{ - ManagedResourceReferencer: fake.ManagedResourceReferencer{ - Ref: &corev1.ObjectReference{ - APIVersion: fake.GVK(&fake.Managed{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Managed{}).Kind, - }, - }, - }, - kind: ManagedKind(fake.GVK(&fake.Managed{})), - want: true, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasManagedResourceReferenceKind(tc.kind)(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasManagedResourceReferenceKind(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestHasClassReferenceKind(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - c client.Client - kind ClassKind - want bool - }{ - "NotAClassReferencer": { - c: &test.MockClient{}, - kind: ClassKind(fake.GVK(&fake.Class{})), - want: false, - }, - "HasNoClassReference": { - obj: &fake.Claim{}, - kind: ClassKind(fake.GVK(&fake.Class{})), - want: false, - }, - "HasCorrectClassReference": { - obj: &fake.Claim{ - ClassReferencer: fake.ClassReferencer{ - Ref: &corev1.ObjectReference{ - APIVersion: fake.GVK(&fake.Class{}).GroupVersion().String(), - Kind: fake.GVK(&fake.Class{}).Kind, - }, - }, - }, - kind: ClassKind(fake.GVK(&fake.Class{})), - want: true, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasClassReferenceKind(tc.kind)(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasClassReferenceKind(...): -want, +got:\n%s", diff) - } - }) - } -} - func TestIsManagedKind(t *testing.T) { cases := map[string]struct { kind ManagedKind @@ -198,7 +114,7 @@ func TestIsManagedKind(t *testing.T) { }, "IsNotKind": { kind: ManagedKind(fake.GVK(&fake.Managed{})), - ot: MockTyper{GVKs: []schema.GroupVersionKind{fake.GVK(&fake.Claim{})}}, + ot: MockTyper{GVKs: []schema.GroupVersionKind{fake.GVK(&fake.Object{})}}, want: false, }, "ErrorDeterminingKind": { @@ -292,11 +208,11 @@ func TestIsPropagator(t *testing.T) { }, "IsPropagator": { obj: func() runtime.Object { - cm := &fake.Claim{} - cm.SetNamespace("somenamespace") - cm.SetName("somename") + o := &fake.Object{} + o.SetNamespace("somenamespace") + o.SetName("somename") mg := &fake.Managed{} - meta.AllowPropagation(mg, cm) + meta.AllowPropagation(mg, o) return mg }(), want: true, @@ -329,12 +245,12 @@ func TestIsPropagated(t *testing.T) { }, "IsPropagated": { obj: func() runtime.Object { - cm := &fake.Claim{} + o := &fake.Object{} mg := &fake.Managed{} mg.SetNamespace("somenamespace") mg.SetName("somename") - meta.AllowPropagation(mg, cm) - return cm + meta.AllowPropagation(mg, o) + return o }(), want: true, }, @@ -349,115 +265,3 @@ func TestIsPropagated(t *testing.T) { }) } } - -func TestHasClassSelector(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - want bool - }{ - "NotAClassSelector": { - want: false, - }, - "NoClassSelector": { - obj: &fake.Claim{}, - want: false, - }, - "HasClassSelector": { - obj: &fake.Claim{ClassSelector: fake.ClassSelector{Sel: &v1.LabelSelector{}}}, - want: true, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasClassSelector()(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasClassSelector(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestHasNoClassSelector(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - want bool - }{ - "NotAClassSelector": { - want: false, - }, - "NoClassSelector": { - obj: &fake.Claim{}, - want: true, - }, - "HasClassSelector": { - obj: &fake.Claim{ClassSelector: fake.ClassSelector{Sel: &v1.LabelSelector{}}}, - want: false, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasNoClassSelector()(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasNoClassSelector(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestHasNoClassReference(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - want bool - }{ - "NotAClassReferencer": { - want: false, - }, - "NoClassReference": { - obj: &fake.Claim{}, - want: true, - }, - "HasClassReference": { - obj: &fake.Claim{ClassReferencer: fake.ClassReferencer{Ref: &corev1.ObjectReference{}}}, - want: false, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasNoClassReference()(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasNoClassReference(...): -want, +got:\n%s", diff) - } - }) - } -} - -func TestHasNoMangedResourceReference(t *testing.T) { - cases := map[string]struct { - obj runtime.Object - want bool - }{ - "NotAManagedResourceReferencer": { - want: false, - }, - "NoManagedResourceReference": { - obj: &fake.Claim{}, - want: true, - }, - "HasClassReference": { - obj: &fake.Claim{ManagedResourceReferencer: fake.ManagedResourceReferencer{Ref: &corev1.ObjectReference{}}}, - want: false, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got := HasNoManagedResourceReference()(tc.obj) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("HasNoManagedResourecReference(...): -want, +got:\n%s", diff) - } - }) - } -} diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 17e8160..9a45b32 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -41,25 +41,9 @@ const SecretTypeConnection corev1.SecretType = "connection.crossplane.io/v1alpha const ( ExternalResourceTagKeyKind = "crossplane-kind" ExternalResourceTagKeyName = "crossplane-name" - ExternalResourceTagKeyClass = "crossplane-class" ExternalResourceTagKeyProvider = "crossplane-providerconfig" ) -// A ClaimKind contains the type metadata for a kind of resource claim. -type ClaimKind schema.GroupVersionKind - -// A ClassKind contains the type metadata for a kind of resource class. -type ClassKind schema.GroupVersionKind - -// List returns the list kind associated with a ClassKind. -func (k ClassKind) List() schema.GroupVersionKind { - return schema.GroupVersionKind{ - Group: k.Group, - Version: k.Version, - Kind: k.Kind + "List", - } -} - // A ManagedKind contains the type metadata for a kind of managed resource. type ManagedKind schema.GroupVersionKind @@ -94,7 +78,7 @@ type ConnectionPropagatorFn func(ctx context.Context, to LocalConnectionSecretOw // A ManagedConnectionPropagator is responsible for propagating information // required to connect to a managed resource (for example the connection secret) -// from the managed resource to its resource claim. +// from the managed resource to a target. type ManagedConnectionPropagator interface { PropagateConnection(ctx context.Context, o LocalConnectionSecretOwner, mg Managed) error } @@ -204,46 +188,6 @@ func IgnoreNotFound(err error) error { return Ignore(kerrors.IsNotFound, err) } -// ResolveClassClaimValues validates the supplied claim value against the -// supplied resource class value. If both are non-zero they must match. -func ResolveClassClaimValues(classValue, claimValue string) (string, error) { - if classValue == "" { - return claimValue, nil - } - if claimValue == "" { - return classValue, nil - } - if classValue != claimValue { - return "", errors.Errorf("claim value [%s] does not match class value [%s]", claimValue, classValue) - } - return claimValue, nil -} - -// SetBindable indicates that the supplied Bindable is ready for binding to -// another Bindable, such as a resource claim or managed resource by setting its -// binding phase to "Unbound". It is a no-op for Bindables in phases "Bound" or -// "Released", because these phases may not transition back to "Unbound". -func SetBindable(b Bindable) { - switch b.GetBindingPhase() { - case v1alpha1.BindingPhaseBound, v1alpha1.BindingPhaseReleased: - return - default: - b.SetBindingPhase(v1alpha1.BindingPhaseUnbound) - } -} - -// IsBindable returns true if the supplied Bindable is ready for binding to -// another Bindable, such as a resource claim or managed -func IsBindable(b Bindable) bool { - return b.GetBindingPhase() == v1alpha1.BindingPhaseUnbound -} - -// IsBound returns true if the supplied Bindable is bound to another Bindable, -// such as a resource claim or managed -func IsBound(b Bindable) bool { - return b.GetBindingPhase() == v1alpha1.BindingPhaseBound -} - // IsConditionTrue returns if condition status is true func IsConditionTrue(c v1alpha1.Condition) bool { return c.Status == corev1.ConditionTrue @@ -373,9 +317,6 @@ func GetExternalTags(mg Managed) map[string]string { ExternalResourceTagKeyKind: strings.ToLower(mg.GetObjectKind().GroupVersionKind().GroupKind().String()), ExternalResourceTagKeyName: mg.GetName(), } - if mg.GetClassReference() != nil { - tags[ExternalResourceTagKeyClass] = mg.GetClassReference().Name - } switch { case mg.GetProviderConfigReference() != nil && mg.GetProviderConfigReference().Name != "": diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go index 4fee555..2f449c7 100644 --- a/pkg/resource/resource_test.go +++ b/pkg/resource/resource_test.go @@ -215,7 +215,7 @@ func TestGetKind(t *testing.T) { "TooManyKinds": { args: args{ ot: MockTyper{GVKs: []schema.GroupVersionKind{ - fake.GVK(&fake.Claim{}), + fake.GVK(&fake.Object{}), fake.GVK(&fake.Managed{}), }}, }, @@ -248,10 +248,10 @@ func TestMustCreateObject(t *testing.T) { }{ "KindRegistered": { args: args{ - kind: fake.GVK(&fake.Claim{}), - oc: fake.SchemeWith(&fake.Claim{}), + kind: fake.GVK(&fake.Managed{}), + oc: fake.SchemeWith(&fake.Managed{}), }, - want: &fake.Claim{}, + want: &fake.Managed{}, }, } @@ -302,86 +302,6 @@ func TestIgnore(t *testing.T) { } } -func TestResolveClassClaimValues(t *testing.T) { - type args struct { - classValue string - claimValue string - } - - type want struct { - err error - value string - } - - cases := map[string]struct { - args - want - }{ - "ClassValueUnset": { - args: args{claimValue: "cool"}, - want: want{value: "cool"}, - }, - "ClaimValueUnset": { - args: args{classValue: "cool"}, - want: want{value: "cool"}, - }, - "IdenticalValues": { - args: args{classValue: "cool", claimValue: "cool"}, - want: want{value: "cool"}, - }, - "ConflictingValues": { - args: args{classValue: "lame", claimValue: "cool"}, - want: want{err: errors.New("claim value [cool] does not match class value [lame]")}, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - got, err := ResolveClassClaimValues(tc.args.classValue, tc.args.claimValue) - if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { - t.Errorf("ResolveClassClaimValues(...): -want error, +got error:\n%s", diff) - } - if diff := cmp.Diff(tc.want.value, got); diff != "" { - t.Errorf("ResolveClassClaimValues(...): -want, +got:\n%s", diff) - } - - }) - } -} - -func TestSetBindable(t *testing.T) { - cases := map[string]struct { - b Bindable - want v1alpha1.BindingPhase - }{ - "BindableIsUnbindable": { - b: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseUnbindable}}, - want: v1alpha1.BindingPhaseUnbound, - }, - "BindableIsUnbound": { - b: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseUnbound}}, - want: v1alpha1.BindingPhaseUnbound, - }, - "BindableIsBound": { - b: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseBound}}, - want: v1alpha1.BindingPhaseBound, - }, - "BindableIsReleased": { - b: &fake.Claim{BindingStatus: v1alpha1.BindingStatus{Phase: v1alpha1.BindingPhaseReleased}}, - want: v1alpha1.BindingPhaseReleased, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - SetBindable(tc.b) - if diff := cmp.Diff(tc.want, tc.b.GetBindingPhase()); diff != "" { - t.Errorf("SetBindable(...): -got, +want:\n%s", diff) - } - }) - } -} - func TestIsConditionTrue(t *testing.T) { cases := map[string]struct { c v1alpha1.Condition @@ -653,7 +573,6 @@ func TestConnectionSecretMustBeControllableBy(t *testing.T) { func TestGetExternalTags(t *testing.T) { provName := "prov" - className := "classy" cases := map[string]struct { o Managed want map[string]string @@ -663,13 +582,11 @@ func TestGetExternalTags(t *testing.T) { Name: name, }, ProviderReferencer: fake.ProviderReferencer{Ref: &v1alpha1.Reference{Name: provName}}, - ClassReferencer: fake.ClassReferencer{Ref: &corev1.ObjectReference{Name: className}}, }, want: map[string]string{ ExternalResourceTagKeyKind: strings.ToLower((&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupKind().String()), ExternalResourceTagKeyName: name, ExternalResourceTagKeyProvider: provName, - ExternalResourceTagKeyClass: className, }, }, "SuccessfulWithProviderConfig": { @@ -677,13 +594,11 @@ func TestGetExternalTags(t *testing.T) { Name: name, }, ProviderConfigReferencer: fake.ProviderConfigReferencer{Ref: &v1alpha1.Reference{Name: provName}}, - ClassReferencer: fake.ClassReferencer{Ref: &corev1.ObjectReference{Name: className}}, }, want: map[string]string{ ExternalResourceTagKeyKind: strings.ToLower((&fake.Managed{}).GetObjectKind().GroupVersionKind().GroupKind().String()), ExternalResourceTagKeyName: name, ExternalResourceTagKeyProvider: provName, - ExternalResourceTagKeyClass: className, }, }, }