Make APIFinalizer interface compatible with all Kubernetes resources

Signed-off-by: Muvaffak Onus <onus.muvaffak@gmail.com>
This commit is contained in:
Muvaffak Onus 2020-04-13 16:31:04 +03:00
parent f9d4e859f4
commit fbeeb71921
No known key found for this signature in database
GPG Key ID: 86E282DC72236827
11 changed files with 199 additions and 363 deletions

View File

@ -190,29 +190,3 @@ func (a *APIStatusBinder) Unbind(ctx context.Context, _ resource.Claim, mg resou
return errors.Wrap(resource.IgnoreNotFound(a.client.Delete(ctx, mg)), errDeleteManaged)
}
// An APIClaimFinalizer adds and removes finalizers to and from a claim.
type APIClaimFinalizer struct {
client client.Client
finalizer string
}
// NewAPIClaimFinalizer returns a new APIClaimFinalizer.
func NewAPIClaimFinalizer(c client.Client, finalizer string) *APIClaimFinalizer {
return &APIClaimFinalizer{client: c, finalizer: finalizer}
}
// AddFinalizer to the supplied Claim.
func (a *APIClaimFinalizer) AddFinalizer(ctx context.Context, cm resource.Claim) error {
if meta.FinalizerExists(cm, a.finalizer) {
return nil
}
meta.AddFinalizer(cm, a.finalizer)
return errors.Wrap(a.client.Update(ctx, cm), errUpdateClaim)
}
// RemoveFinalizer from the supplied Claim.
func (a *APIClaimFinalizer) RemoveFinalizer(ctx context.Context, cm resource.Claim) error {
meta.RemoveFinalizer(cm, a.finalizer)
return errors.Wrap(resource.IgnoreNotFound(a.client.Update(ctx, cm)), errUpdateClaim)
}

View File

@ -38,7 +38,6 @@ var (
_ ManagedCreator = &APIManagedCreator{}
_ Binder = &APIBinder{}
_ Binder = &APIStatusBinder{}
_ ClaimFinalizer = &APIClaimFinalizer{}
)
func TestCreate(t *testing.T) {
@ -575,6 +574,7 @@ func TestUnbind(t *testing.T) {
})
}
}
func TestStatusUnbind(t *testing.T) {
type args struct {
ctx context.Context
@ -717,119 +717,3 @@ func TestStatusUnbind(t *testing.T) {
})
}
}
func TestClaimRemoveFinalizer(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
cm resource.Claim
}
type want struct {
err error
cm resource.Claim
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateClaimError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateClaim),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: nil,
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIClaimFinalizer(tc.client, finalizer)
err := api.RemoveFinalizer(tc.args.ctx, tc.args.cm)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.Finalize(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.cm, tc.args.cm, test.EquateConditions()); diff != "" {
t.Errorf("api.Finalize(...) Claim: -want, +got:\n%s", diff)
}
})
}
}
func TestAPIClaimFinalizerAdder(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
cm resource.Claim
}
type want struct {
err error
cm resource.Claim
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateClaimError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateClaim),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: nil,
cm: &fake.Claim{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIClaimFinalizer(tc.client, finalizer)
err := api.AddFinalizer(tc.args.ctx, tc.args.cm)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.Initialize(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.cm, tc.args.cm, test.EquateConditions()); diff != "" {
t.Errorf("api.Initialize(...) Claim: -want, +got:\n%s", diff)
}
})
}
}

View File

@ -134,31 +134,6 @@ func (b BinderFns) Unbind(ctx context.Context, cm resource.Claim, mg resource.Ma
return b.UnbindFn(ctx, cm, mg)
}
// A ClaimFinalizer finalizes the deletion of a resource claim.
type ClaimFinalizer interface {
// AddFinalizer to the supplied Claim.
AddFinalizer(ctx context.Context, cm resource.Claim) error
// RemoveFinalizer from the supplied Claim.
RemoveFinalizer(ctx context.Context, cm resource.Claim) error
}
// A ClaimFinalizerFns satisfy the ClaimFinalizer interface.
type ClaimFinalizerFns struct {
AddFinalizerFn func(ctx context.Context, cm resource.Claim) error
RemoveFinalizerFn func(ctx context.Context, cm resource.Claim) error
}
// AddFinalizer to the supplied Claim.
func (f ClaimFinalizerFns) AddFinalizer(ctx context.Context, cm resource.Claim) error {
return f.AddFinalizerFn(ctx, cm)
}
// RemoveFinalizer from the supplied Claim.
func (f ClaimFinalizerFns) RemoveFinalizer(ctx context.Context, cm resource.Claim) error {
return f.RemoveFinalizerFn(ctx, cm)
}
// 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
@ -199,14 +174,14 @@ func defaultCRManaged(m manager.Manager) crManaged {
}
type crClaim struct {
ClaimFinalizer
resource.Finalizer
Binder
}
func defaultCRClaim(m manager.Manager) crClaim {
return crClaim{
ClaimFinalizer: NewAPIClaimFinalizer(m.GetClient(), claimFinalizerName),
Binder: NewAPIStatusBinder(m.GetClient(), m.GetScheme()),
Finalizer: resource.NewAPIFinalizer(m.GetClient(), claimFinalizerName),
Binder: NewAPIStatusBinder(m.GetClient(), m.GetScheme()),
}
}
@ -248,9 +223,9 @@ func WithBinder(b Binder) ReconcilerOption {
// WithClaimFinalizer specifies which ClaimFinalizer should be used to finalize
// claims when they are deleted.
func WithClaimFinalizer(f ClaimFinalizer) ReconcilerOption {
func WithClaimFinalizer(f resource.Finalizer) ReconcilerOption {
return func(r *Reconciler) {
r.claim.ClaimFinalizer = f
r.claim.Finalizer = f
}
}

View File

@ -210,7 +210,7 @@ func TestReconciler(t *testing.T) {
with: resource.ManagedKind(fake.GVK(&fake.Managed{})),
o: []ReconcilerOption{
WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}),
WithClaimFinalizer(ClaimFinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Claim) error { return nil }}),
WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{Requeue: false}},
@ -247,7 +247,7 @@ func TestReconciler(t *testing.T) {
with: resource.ManagedKind(fake.GVK(&fake.Managed{})),
o: []ReconcilerOption{
WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}),
WithClaimFinalizer(ClaimFinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Claim) error { return errBoom }}),
WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: aShortWait}},
@ -275,7 +275,7 @@ func TestReconciler(t *testing.T) {
with: resource.ManagedKind(fake.GVK(&fake.Managed{})),
o: []ReconcilerOption{
WithBinder(BinderFns{UnbindFn: func(_ context.Context, _ resource.Claim, _ resource.Managed) error { return nil }}),
WithClaimFinalizer(ClaimFinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Claim) error { return nil }}),
WithClaimFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{Requeue: false}},
@ -421,8 +421,8 @@ func TestReconciler(t *testing.T) {
WithManagedConfigurators(ManagedConfiguratorFn(
func(_ context.Context, _ resource.Claim, _ resource.Class, _ resource.Managed) error { return nil },
)),
WithClaimFinalizer(ClaimFinalizerFns{
AddFinalizerFn: func(_ context.Context, _ resource.Claim) 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 },
@ -599,8 +599,8 @@ func TestReconciler(t *testing.T) {
WithManagedConnectionPropagator(resource.ManagedConnectionPropagatorFn(
func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.Managed) error { return nil },
)),
WithClaimFinalizer(ClaimFinalizerFns{
AddFinalizerFn: func(_ context.Context, _ resource.Claim) error { return errBoom }},
WithClaimFinalizer(resource.FinalizerFns{
AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }},
),
},
},
@ -646,8 +646,8 @@ func TestReconciler(t *testing.T) {
WithManagedConnectionPropagator(resource.ManagedConnectionPropagatorFn(
func(_ context.Context, _ resource.LocalConnectionSecretOwner, _ resource.Managed) error { return nil },
)),
WithClaimFinalizer(ClaimFinalizerFns{
AddFinalizerFn: func(_ context.Context, _ resource.Claim) 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 },

View File

@ -34,32 +34,6 @@ const (
errUpdateManagedStatus = "cannot update managed resource status"
)
// An APIFinalizer adds and removes finalizers to and from a resource.
type APIFinalizer struct {
client client.Client
finalizer string
}
// NewAPIFinalizer returns a new APIFinalizer.
func NewAPIFinalizer(c client.Client, finalizer string) *APIFinalizer {
return &APIFinalizer{client: c, finalizer: finalizer}
}
// AddFinalizer to the supplied Managed resource.
func (a *APIFinalizer) AddFinalizer(ctx context.Context, mg resource.Managed) error {
if meta.FinalizerExists(mg, a.finalizer) {
return nil
}
meta.AddFinalizer(mg, a.finalizer)
return errors.Wrap(a.client.Update(ctx, mg), errUpdateManaged)
}
// RemoveFinalizer from the supplied Managed resource.
func (a *APIFinalizer) RemoveFinalizer(ctx context.Context, mg resource.Managed) error {
meta.RemoveFinalizer(mg, a.finalizer)
return errors.Wrap(resource.IgnoreNotFound(a.client.Update(ctx, mg)), errUpdateManaged)
}
// NameAsExternalName writes the name of the managed resource to
// the external name annotation field in order to be used as name of
// the external resource in provider.

View File

@ -34,126 +34,9 @@ import (
)
var (
_ Finalizer = &APIFinalizer{}
_ Initializer = &NameAsExternalName{}
)
func TestManagedRemoveFinalizer(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
mg resource.Managed
}
type want struct {
err error
mg resource.Managed
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateManagedError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateManaged),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: nil,
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIFinalizer(tc.client, finalizer)
err := api.RemoveFinalizer(tc.args.ctx, tc.args.mg)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.RemoveFinalizer(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" {
t.Errorf("api.RemoveFinalizer(...) Managed: -want, +got:\n%s", diff)
}
})
}
}
func TestAPIFinalizerAdder(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
mg resource.Managed
}
type want struct {
err error
mg resource.Managed
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateManagedError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateManaged),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: nil,
mg: &fake.Managed{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIFinalizer(tc.client, finalizer)
err := api.AddFinalizer(tc.args.ctx, tc.args.mg)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.Initialize(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.mg, tc.args.mg, test.EquateConditions()); diff != "" {
t.Errorf("api.Initialize(...) Managed: -want, +got:\n%s", diff)
}
})
}
}
func TestNameAsExternalName(t *testing.T) {
type args struct {
ctx context.Context

View File

@ -130,31 +130,6 @@ func (cc InitializerChain) Initialize(ctx context.Context, mg resource.Managed)
return nil
}
// A Finalizer finalizes the deletion of a resource claim.
type Finalizer interface {
// AddFinalizer to the supplied Managed resource.
AddFinalizer(ctx context.Context, mg resource.Managed) error
// RemoveFinalizer from the supplied Managed resource.
RemoveFinalizer(ctx context.Context, mg resource.Managed) error
}
// A FinalizerFns satisfy the Finalizer interface.
type FinalizerFns struct {
AddFinalizerFn func(ctx context.Context, mg resource.Managed) error
RemoveFinalizerFn func(ctx context.Context, mg resource.Managed) error
}
// AddFinalizer to the supplied Managed resource.
func (f FinalizerFns) AddFinalizer(ctx context.Context, mg resource.Managed) error {
return f.AddFinalizerFn(ctx, mg)
}
// RemoveFinalizer from the supplied Managed resource.
func (f FinalizerFns) RemoveFinalizer(ctx context.Context, mg resource.Managed) error {
return f.RemoveFinalizerFn(ctx, mg)
}
// A InitializerFn is a function that satisfies the Initializer
// interface.
type InitializerFn func(ctx context.Context, mg resource.Managed) error
@ -333,7 +308,7 @@ type Reconciler struct {
type mrManaged struct {
ConnectionPublisher
Finalizer
resource.Finalizer
Initializer
ReferenceResolver
}
@ -341,7 +316,7 @@ type mrManaged struct {
func defaultMRManaged(m manager.Manager) mrManaged {
return mrManaged{
ConnectionPublisher: NewAPISecretPublisher(m.GetClient(), m.GetScheme()),
Finalizer: NewAPIFinalizer(m.GetClient(), managedFinalizerName),
Finalizer: resource.NewAPIFinalizer(m.GetClient(), managedFinalizerName),
Initializer: NewNameAsExternalName(m.GetClient()),
ReferenceResolver: NewAPIReferenceResolver(m.GetClient()),
}
@ -417,7 +392,7 @@ func WithInitializers(i ...Initializer) ReconcilerOption {
// WithFinalizer specifies how the Reconciler should add and remove
// finalizers to and from the managed resource.
func WithFinalizer(f Finalizer) ReconcilerOption {
func WithFinalizer(f resource.Finalizer) ReconcilerOption {
return func(r *Reconciler) {
r.managed.Finalizer = f
}

View File

@ -396,7 +396,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Managed) error { return errBoom }}),
WithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -427,7 +427,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{RemoveFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{Requeue: false}},
@ -488,7 +488,7 @@ func TestReconciler(t *testing.T) {
WithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.CanReference) error { return nil })),
WithExternalConnecter(&NopConnecter{}),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return errBoom }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return errBoom }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -528,7 +528,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -579,7 +579,7 @@ func TestReconciler(t *testing.T) {
return nil
},
}),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -609,7 +609,7 @@ func TestReconciler(t *testing.T) {
WithReferenceResolver(ReferenceResolverFn(func(_ context.Context, _ resource.CanReference) error { return nil })),
WithExternalConnecter(&NopConnecter{}),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -646,7 +646,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedLongWait}},
@ -686,7 +686,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -737,7 +737,7 @@ func TestReconciler(t *testing.T) {
return nil
},
}),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedShortWait}},
@ -777,7 +777,7 @@ func TestReconciler(t *testing.T) {
return c, nil
})),
WithConnectionPublishers(),
WithFinalizer(FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Managed) error { return nil }}),
WithFinalizer(resource.FinalizerFns{AddFinalizerFn: func(_ context.Context, _ resource.Object) error { return nil }}),
},
},
want: want{result: reconcile.Result{RequeueAfter: defaultManagedLongWait}},

View File

@ -37,6 +37,8 @@ const (
errSecretConflict = "cannot establish control of existing connection secret"
errUpdateSecret = "cannot update connection secret"
errCreateOrUpdateSecret = "cannot create or update connection secret"
errUpdateObject = "cannot update object"
)
// An APIManagedConnectionPropagator propagates connection details by reading
@ -176,3 +178,48 @@ func (a *APIUpdatingApplicator) Apply(ctx context.Context, o runtime.Object, ao
return errors.Wrap(a.client.Update(ctx, o), "cannot update object")
}
// An APIFinalizer adds and removes finalizers to and from a resource.
type APIFinalizer struct {
client client.Client
finalizer string
}
// NewAPIFinalizer returns a new APIFinalizer.
func NewAPIFinalizer(c client.Client, finalizer string) *APIFinalizer {
return &APIFinalizer{client: c, finalizer: finalizer}
}
// AddFinalizer to the supplied Managed resource.
func (a *APIFinalizer) AddFinalizer(ctx context.Context, obj Object) error {
if meta.FinalizerExists(obj, a.finalizer) {
return nil
}
meta.AddFinalizer(obj, a.finalizer)
return errors.Wrap(a.client.Update(ctx, obj), errUpdateObject)
}
// RemoveFinalizer from the supplied Managed resource.
func (a *APIFinalizer) RemoveFinalizer(ctx context.Context, obj Object) error {
if !meta.FinalizerExists(obj, a.finalizer) {
return nil
}
meta.RemoveFinalizer(obj, a.finalizer)
return errors.Wrap(IgnoreNotFound(a.client.Update(ctx, obj)), errUpdateObject)
}
// A FinalizerFns satisfy the Finalizer interface.
type FinalizerFns struct {
AddFinalizerFn func(ctx context.Context, obj Object) error
RemoveFinalizerFn func(ctx context.Context, obj Object) error
}
// AddFinalizer to the supplied resource.
func (f FinalizerFns) AddFinalizer(ctx context.Context, obj Object) error {
return f.AddFinalizerFn(ctx, obj)
}
// RemoveFinalizer from the supplied resource.
func (f FinalizerFns) RemoveFinalizer(ctx context.Context, obj Object) error {
return f.RemoveFinalizerFn(ctx, obj)
}

View File

@ -490,3 +490,119 @@ func TestAPIUpdatingApplicator(t *testing.T) {
})
}
}
func TestManagedRemoveFinalizer(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
obj Object
}
type want struct {
err error
obj Object
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateObject),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
want: want{
err: nil,
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIFinalizer(tc.client, finalizer)
err := api.RemoveFinalizer(tc.args.ctx, tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.RemoveFinalizer(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.obj, tc.args.obj, test.EquateConditions()); diff != "" {
t.Errorf("api.RemoveFinalizer(...) Managed: -want, +got:\n%s", diff)
}
})
}
}
func TestAPIFinalizerAdder(t *testing.T) {
finalizer := "veryfinal"
type args struct {
ctx context.Context
obj Object
}
type want struct {
err error
obj Object
}
errBoom := errors.New("boom")
cases := map[string]struct {
client client.Client
args args
want want
}{
"UpdateError": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(errBoom)},
args: args{
ctx: context.Background(),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: errors.Wrap(errBoom, errUpdateObject),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
"Successful": {
client: &test.MockClient{MockUpdate: test.NewMockUpdateFn(nil)},
args: args{
ctx: context.Background(),
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{}}},
},
want: want{
err: nil,
obj: &fake.Object{ObjectMeta: metav1.ObjectMeta{Finalizers: []string{finalizer}}},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
api := NewAPIFinalizer(tc.client, finalizer)
err := api.AddFinalizer(tc.args.ctx, tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("api.Initialize(...): -want error, +got error:\n%s", diff)
}
if diff := cmp.Diff(tc.want.obj, tc.args.obj, test.EquateConditions()); diff != "" {
t.Errorf("api.Initialize(...) Managed: -want, +got:\n%s", diff)
}
})
}
}

View File

@ -17,6 +17,8 @@ limitations under the License.
package resource
import (
"context"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -101,6 +103,12 @@ type WorkloadReferencer interface {
SetWorkloadReference(v1alpha1.TypedReference)
}
// A Finalizer manages the finalizers on the resource.
type Finalizer interface {
AddFinalizer(ctx context.Context, obj Object) error
RemoveFinalizer(ctx context.Context, obj Object) error
}
// An Object is a Kubernetes object.
type Object interface {
metav1.Object