Introduce a deletion policy, and deprecate the reclaim policy.

The deletion policy is a more narrowly scoped variant of the reclaim policy. It
affects only whether exeternal resources are deleted or orphaned when their
corresponding managed resource is deleted, as opposed to the reclaim policy
which also affects whether the managed resource is deleted when its bound claim
is deleted.

Signed-off-by: Nic Cope <negz@rk0n.org>
This commit is contained in:
Nic Cope 2020-08-11 18:24:16 -07:00
parent 8c96c010f5
commit 5c5d8932d3
6 changed files with 112 additions and 1 deletions

View File

@ -30,3 +30,17 @@ const (
// will be retained when the managed resource is deleted. // will be retained when the managed resource is deleted.
ReclaimRetain ReclaimPolicy = "Retain" ReclaimRetain ReclaimPolicy = "Retain"
) )
// A DeletionPolicy determines what should happen to the underlying external
// resource when a managed resource is deleted.
type DeletionPolicy string
const (
// DeletionOrphan means the external resource will orphaned when its managed
// resource is deleted.
DeletionOrphan DeletionPolicy = "Orphan"
// DeletionDelete means both the external resource will be deleted when its
// managed resource is deleted.
DeletionDelete DeletionPolicy = "Delete"
)

View File

@ -201,6 +201,14 @@ type ResourceSpec struct {
// observe, update, and delete this managed resource. // observe, update, and delete this managed resource.
ProviderReference Reference `json:"providerRef"` ProviderReference Reference `json:"providerRef"`
// DeletionPolicy specifies what will happen to the underlying external
// when this managed resource is deleted. The "Orphan" policy is used when
// no policy is specified.
//
// +optional
// +kubebuilder:validation:Enum=Orphan;Delete
DeletionPolicy DeletionPolicy `json:"deletionPolicy,omitempty"`
// ReclaimPolicy specifies what will happen to this managed resource when // ReclaimPolicy specifies what will happen to this managed resource when
// its resource claim is deleted, and what will happen to the underlying // its resource claim is deleted, and what will happen to the underlying
// external resource when the managed resource is deleted. The "Delete" // external resource when the managed resource is deleted. The "Delete"
@ -211,6 +219,10 @@ type ResourceSpec struct {
// resource claim is deleted, and in turn causes the external resource to be // resource claim is deleted, and in turn causes the external resource to be
// retained when its managed resource is deleted. The "Retain" policy is // retained when its managed resource is deleted. The "Retain" policy is
// used when no policy is specified. // used when no policy is specified.
//
// Deprecated. DeletionPolicy takes precedence when both are set.
// See https://github.com/crossplane/crossplane-runtime/issues/179.
//
// +optional // +optional
// +kubebuilder:validation:Enum=Retain;Delete // +kubebuilder:validation:Enum=Retain;Delete
ReclaimPolicy ReclaimPolicy `json:"reclaimPolicy,omitempty"` ReclaimPolicy ReclaimPolicy `json:"reclaimPolicy,omitempty"`

View File

@ -545,7 +545,7 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error)
if meta.WasDeleted(managed) { if meta.WasDeleted(managed) {
log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp()) log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp())
if observation.ResourceExists && managed.GetReclaimPolicy() == v1alpha1.ReclaimDelete { if observation.ResourceExists && shouldDelete(managed) {
if err := external.Delete(externalCtx, managed); err != nil { if err := external.Delete(externalCtx, managed); err != nil {
// We'll hit this condition if we can't delete our external // We'll hit this condition if we can't delete our external
// resource, for example if our provider credentials don't have // resource, for example if our provider credentials don't have
@ -695,3 +695,16 @@ func (r *Reconciler) Reconcile(req reconcile.Request) (reconcile.Result, error)
managed.SetConditions(v1alpha1.ReconcileSuccess()) managed.SetConditions(v1alpha1.ReconcileSuccess())
return reconcile.Result{RequeueAfter: r.longWait}, errors.Wrap(r.client.Status().Update(ctx, managed), errUpdateManagedStatus) 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 {
case mg.GetDeletionPolicy() == v1alpha1.DeletionDelete:
return true
case mg.GetDeletionPolicy() == v1alpha1.DeletionOrphan:
return false
}
return mg.GetReclaimPolicy() == v1alpha1.ReclaimDelete
}

View File

@ -759,3 +759,58 @@ 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{
Deletable: fake.Deletable{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{
Deletable: fake.Deletable{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 not be deleted when no deletion or reclaim policy is specified.",
mg: &fake.Managed{
Reclaimer: fake.Reclaimer{},
},
want: false,
},
}
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)
}
})
}
}

View File

@ -135,6 +135,15 @@ func (m *Reclaimer) SetReclaimPolicy(p v1alpha1.ReclaimPolicy) { m.Policy = p }
// GetReclaimPolicy gets the ReclaimPolicy. // GetReclaimPolicy gets the ReclaimPolicy.
func (m *Reclaimer) GetReclaimPolicy() v1alpha1.ReclaimPolicy { return m.Policy } func (m *Reclaimer) GetReclaimPolicy() v1alpha1.ReclaimPolicy { return m.Policy }
// Deletable implements the Deletable interface.
type Deletable struct{ Policy v1alpha1.DeletionPolicy }
// SetDeletionPolicy sets the DeletionPolicy.
func (m *Deletable) SetDeletionPolicy(p v1alpha1.DeletionPolicy) { m.Policy = p }
// GetDeletionPolicy gets the DeletionPolicy.
func (m *Deletable) GetDeletionPolicy() v1alpha1.DeletionPolicy { return m.Policy }
// CredentialsSecretReferencer is a mock that satisfies CredentialsSecretReferencer // CredentialsSecretReferencer is a mock that satisfies CredentialsSecretReferencer
// interface. // interface.
type CredentialsSecretReferencer struct{ Ref v1alpha1.SecretKeySelector } type CredentialsSecretReferencer struct{ Ref v1alpha1.SecretKeySelector }
@ -272,6 +281,7 @@ type Managed struct {
ClaimReferencer ClaimReferencer
ProviderReferencer ProviderReferencer
ConnectionSecretWriterTo ConnectionSecretWriterTo
Deletable
Reclaimer Reclaimer
v1alpha1.ConditionedStatus v1alpha1.ConditionedStatus
v1alpha1.BindingStatus v1alpha1.BindingStatus

View File

@ -84,6 +84,12 @@ type Reclaimer interface {
GetReclaimPolicy() v1alpha1.ReclaimPolicy GetReclaimPolicy() v1alpha1.ReclaimPolicy
} }
// A Deletable may specify a DeletionPolicy.
type Deletable interface {
SetDeletionPolicy(p v1alpha1.DeletionPolicy)
GetDeletionPolicy() v1alpha1.DeletionPolicy
}
// A CredentialsSecretReferencer may refer to a credential secret in an arbitrary // A CredentialsSecretReferencer may refer to a credential secret in an arbitrary
// namespace. // namespace.
type CredentialsSecretReferencer interface { type CredentialsSecretReferencer interface {
@ -187,6 +193,7 @@ type Managed interface {
ClaimReferencer ClaimReferencer
ProviderReferencer ProviderReferencer
ConnectionSecretWriterTo ConnectionSecretWriterTo
Deletable
Reclaimer Reclaimer
Conditioned Conditioned