Skip to content

Commit

Permalink
Add options to client.Delete
Browse files Browse the repository at this point in the history
Allows Delete requests to carry options like GracePeriodSeconds,
Preconditions, and PropagationPolicy.

To use the default options, call Delete with the same signature as
before:

client.Delete(context.TODO(), obj)

Delete options are passed to the request using the functional options
pattern. This Delete call sets a GracePeriodSeconds:

client.Delete(context.TODO(), obj, client.GracePeriodSeconds(1))

Multiple options can be set:

client.Delete(context.TODO(), obj,
  client.GracePeriodSeconds(1),
  client.PropagationPolicy(metav1.DeletePropagationForeground))

The fake client doesn't yet implement delete options like propagation,
so keep that in mind when writing tests.
  • Loading branch information
grantr committed Sep 6, 2018
1 parent d4dff20 commit 7571dae
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 3 deletions.
5 changes: 4 additions & 1 deletion pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,18 @@ func (c *client) Update(ctx context.Context, obj runtime.Object) error {
}

// Delete implements client.Client
func (c *client) Delete(ctx context.Context, obj runtime.Object) error {
func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error {
o, err := c.cache.getObjMeta(obj)
if err != nil {
return err
}

deleteOpts := DeleteOptions{}
return o.Delete().
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
Body(deleteOpts.ApplyOptions(opts).AsDeleteOptions()).
Do().
Error()
}
Expand Down
46 changes: 46 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,52 @@ var _ = Describe("Client", func() {
})
})

Describe("DeleteOptions", func() {
It("should allow setting GracePeriodSeconds", func() {
do := &client.DeleteOptions{}
client.GracePeriodSeconds(1)(do)
gp := int64(1)
Expect(do.AsDeleteOptions().GracePeriodSeconds).To(Equal(&gp))
})

It("should allow setting Precondition", func() {
do := &client.DeleteOptions{}
pc := metav1.NewUIDPreconditions("uid")
client.Preconditions(pc)(do)
Expect(do.AsDeleteOptions().Preconditions).To(Equal(pc))
Expect(do.Preconditions).To(Equal(pc))
})

It("should allow setting PropagationPolicy", func() {
do := &client.DeleteOptions{}
client.PropagationPolicy(metav1.DeletePropagationForeground)(do)
dp := metav1.DeletePropagationForeground
Expect(do.AsDeleteOptions().PropagationPolicy).To(Equal(&dp))
})

It("should produce empty metav1.DeleteOptions if nil", func() {
var do *client.DeleteOptions
Expect(do.AsDeleteOptions()).To(Equal(&metav1.DeleteOptions{}))
do = &client.DeleteOptions{}
Expect(do.AsDeleteOptions()).To(Equal(&metav1.DeleteOptions{}))
})

It("should merge multiple options together", func() {
gp := int64(1)
pc := metav1.NewUIDPreconditions("uid")
dp := metav1.DeletePropagationForeground
do := &client.DeleteOptions{}
do.ApplyOptions([]client.DeleteOptionFunc{
client.GracePeriodSeconds(gp),
client.Preconditions(pc),
client.PropagationPolicy(dp),
})
Expect(do.GracePeriodSeconds).To(Equal(&gp))
Expect(do.Preconditions).To(Equal(pc))
Expect(do.PropagationPolicy).To(Equal(&dp))
})
})

Describe("ListOptions", func() {
It("should be able to set a LabelSelector", func() {
lo := &client.ListOptions{}
Expand Down
3 changes: 2 additions & 1 deletion pkg/client/fake/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (c *fakeClient) Create(ctx context.Context, obj runtime.Object) error {
return c.tracker.Create(gvr, obj, accessor.GetNamespace())
}

func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object) error {
func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...client.DeleteOptionFunc) error {
gvr, err := getGVRFromObject(obj)
if err != nil {
return err
Expand All @@ -114,6 +114,7 @@ func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object) error {
if err != nil {
return err
}
//TODO: implement propagation
return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
}

Expand Down
84 changes: 83 additions & 1 deletion pkg/client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Writer interface {
Create(ctx context.Context, obj runtime.Object) error

// Delete deletes the given obj from Kubernetes cluster.
Delete(ctx context.Context, obj runtime.Object) error
Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error

// Update updates the given obj in the Kubernetes cluster. obj must be a
// struct pointer so that obj can be updated with the content returned by the Server.
Expand Down Expand Up @@ -93,6 +93,88 @@ type FieldIndexer interface {
IndexField(obj runtime.Object, field string, extractValue IndexerFunc) error
}

// DeleteOptions contains options for delete requests. It's generally a subset
// of metav1.DeleteOptions.
type DeleteOptions struct {
// GracePeriodSeconds is the duration in seconds before the object should be
// deleted. Value must be non-negative integer. The value zero indicates
// delete immediately. If this value is nil, the default grace period for the
// specified type will be used.
GracePeriodSeconds *int64

// Preconditions must be fulfilled before a deletion is carried out. If not
// possible, a 409 Conflict status will be returned.
Preconditions *metav1.Preconditions

// PropagationPolicy determined whether and how garbage collection will be
// performed. Either this field or OrphanDependents may be set, but not both.
// The default policy is decided by the existing finalizer set in the
// metadata.finalizers and the resource-specific default policy.
// Acceptable values are: 'Orphan' - orphan the dependents; 'Background' -
// allow the garbage collector to delete the dependents in the background;
// 'Foreground' - a cascading policy that deletes all dependents in the
// foreground.
PropagationPolicy *metav1.DeletionPropagation

// Raw represents raw DeleteOptions, as passed to the API server.
Raw *metav1.DeleteOptions
}

// AsDeleteOptions returns these options as a metav1.DeleteOptions.
// This may mutate the Raw field.
func (o *DeleteOptions) AsDeleteOptions() *metav1.DeleteOptions {

if o == nil {
return &metav1.DeleteOptions{}
}
if o.Raw == nil {
o.Raw = &metav1.DeleteOptions{}
}

o.Raw.GracePeriodSeconds = o.GracePeriodSeconds
o.Raw.Preconditions = o.Preconditions
o.Raw.PropagationPolicy = o.PropagationPolicy
return o.Raw
}

// ApplyOptions executes the given DeleteOptionFuncs and returns the mutated
// DeleteOptions.
func (o *DeleteOptions) ApplyOptions(optFuncs []DeleteOptionFunc) *DeleteOptions {
for _, optFunc := range optFuncs {
optFunc(o)
}
return o
}

// DeleteOptionFunc is a function that mutates a DeleteOptions struct. It implements
// the functional options pattern. See
// https://github.com/tmrts/go-patterns/blob/master/idiom/functional-options.md.
type DeleteOptionFunc func(*DeleteOptions)

// GracePeriodSeconds is a functional option that sets the GracePeriodSeconds
// field of a DeleteOptions struct.
func GracePeriodSeconds(gp int64) DeleteOptionFunc {
return func(opts *DeleteOptions) {
opts.GracePeriodSeconds = &gp
}
}

// Preconditions is a functional option that sets the Preconditions field of a
// DeleteOptions struct.
func Preconditions(p *metav1.Preconditions) DeleteOptionFunc {
return func(opts *DeleteOptions) {
opts.Preconditions = p
}
}

// PropagationPolicy is a functional option that sets the PropagationPolicy
// field of a DeleteOptions struct.
func PropagationPolicy(p metav1.DeletionPropagation) DeleteOptionFunc {
return func(opts *DeleteOptions) {
opts.PropagationPolicy = &p
}
}

// ListOptions contains options for limitting or filtering results.
// It's generally a subset of metav1.ListOptions, with support for
// pre-parsed selectors (since generally, selectors will be executed
Expand Down

0 comments on commit 7571dae

Please sign in to comment.