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.
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.
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
// its resource claim is deleted, and what will happen to the underlying
// 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
// retained when its managed resource is deleted. The "Retain" 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"`

View File

@ -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 && managed.GetReclaimPolicy() == v1alpha1.ReclaimDelete {
if observation.ResourceExists && shouldDelete(managed) {
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,3 +695,16 @@ 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 {
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.
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
// interface.
type CredentialsSecretReferencer struct{ Ref v1alpha1.SecretKeySelector }
@ -272,6 +281,7 @@ type Managed struct {
ClaimReferencer
ProviderReferencer
ConnectionSecretWriterTo
Deletable
Reclaimer
v1alpha1.ConditionedStatus
v1alpha1.BindingStatus

View File

@ -84,6 +84,12 @@ type Reclaimer interface {
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
// namespace.
type CredentialsSecretReferencer interface {
@ -187,6 +193,7 @@ type Managed interface {
ClaimReferencer
ProviderReferencer
ConnectionSecretWriterTo
Deletable
Reclaimer
Conditioned