Skip to content
This repository has been archived by the owner on Nov 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #92 from ericchiang/delete-options
Browse files Browse the repository at this point in the history
*: add options for setting the body of a DELETE request
  • Loading branch information
ericchiang authored Jul 25, 2018
2 parents e52016d + a7fdea8 commit f2ca415
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 25 deletions.
11 changes: 10 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,16 @@ func (c *Client) Delete(ctx context.Context, req Resource, options ...Option) er
if err != nil {
return err
}
return c.do(ctx, "DELETE", url, nil, nil)
o := &deleteOptions{
Kind: "DeleteOptions",
APIVersion: "v1",
PropagationPolicy: "Background",
}
for _, option := range options {
option.updateDelete(req, o)
}

return c.do(ctx, "DELETE", url, o, nil)
}

func (c *Client) Update(ctx context.Context, req Resource, options ...Option) error {
Expand Down
113 changes: 113 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,119 @@ func TestDefaultNamespace(t *testing.T) {
}
}

func TestDelete(t *testing.T) {
t.Skip("this test requires a controller manager to run, see issue #95")

withNamespace(t, func(client *k8s.Client, namespace string) {
cm := &corev1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: k8s.String("my-configmap"),
Namespace: &namespace,
},
}
if err := client.Create(context.TODO(), cm); err != nil {
t.Errorf("create configmap: %v", err)
return
}

cm2 := &corev1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: k8s.String("my-configmap-2"),
Namespace: &namespace,
OwnerReferences: []*metav1.OwnerReference{
{
ApiVersion: k8s.String("v1"),
Kind: k8s.String("ConfigMap"),
Name: cm.Metadata.Name,
Uid: cm.Metadata.Uid,
BlockOwnerDeletion: k8s.Bool(true),
},
},
},
}
if err := client.Create(context.TODO(), cm2); err != nil {
t.Errorf("create configmap: %v", err)
return
}

if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationForeground()); err != nil {
t.Fatalf("delete configmap: %v", err)
}

var err error
for i := 0; i < 50; i++ {
err = func() error {
err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2)
if err == nil {
return fmt.Errorf("expected 404 error")
}
apiErr, ok := err.(*k8s.APIError)
if !ok {
return fmt.Errorf("error was not of type APIError: %T %v", err, err)
}
if apiErr.Code != 404 {
return fmt.Errorf("expected 404 error code, got %d", apiErr.Code)
}
return nil
}()
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
if err != nil {
t.Errorf("getting parent configmap: %v", err)
}
})
}

func TestDeleteOrphan(t *testing.T) {
withNamespace(t, func(client *k8s.Client, namespace string) {
cm := &corev1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: k8s.String("my-configmap"),
Namespace: &namespace,
},
}
if err := client.Create(context.TODO(), cm); err != nil {
t.Errorf("create configmap: %v", err)
return
}

cm2 := &corev1.ConfigMap{
Metadata: &metav1.ObjectMeta{
Name: k8s.String("my-configmap-2"),
Namespace: &namespace,
OwnerReferences: []*metav1.OwnerReference{
{
ApiVersion: k8s.String("v1"),
Kind: k8s.String("ConfigMap"),
Name: cm.Metadata.Name,
Uid: cm.Metadata.Uid,
},
},
},
}
if err := client.Create(context.TODO(), cm2); err != nil {
t.Errorf("create configmap: %v", err)
return
}

if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationOrphan()); err != nil {
t.Fatalf("delete configmap: %v", err)
}

// We're attempting to affirm a negative, that this won't eventually be deleted.
// Since there's no explicit even for us to wait for
time.Sleep(time.Second)

err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2)
if err != nil {
t.Errorf("failed to get configmap: %v", err)
}
})
}

func Test404(t *testing.T) {
withNamespace(t, func(client *k8s.Client, namespace string) {
var configMap corev1.ConfigMap
Expand Down
2 changes: 1 addition & 1 deletion labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type LabelSelector struct {
}

func (l *LabelSelector) Selector() Option {
return queryParam{"labelSelector", l.String()}
return QueryParam("labelSelector", l.String())
}

func (l *LabelSelector) String() string {
Expand Down
91 changes: 68 additions & 23 deletions resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,90 @@ import (
// Option represents optional call parameters, such as label selectors.
type Option interface {
updateURL(base string, v url.Values) string
updateDelete(r Resource, d *deleteOptions)
}

type queryParam struct {
paramName string
paramValue string
}
type optionFunc func(base string, v url.Values) string

func (f optionFunc) updateDelete(r Resource, d *deleteOptions) {}
func (f optionFunc) updateURL(base string, v url.Values) string { return f(base, v) }

type deleteOptions struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`

func (o queryParam) updateURL(base string, v url.Values) string {
v.Set(o.paramName, o.paramValue)
return base
GracePeriod *int64 `json:"gracePeriodSeconds,omitempty"`
Preconditions struct {
UID string `json:"uid,omitempty"`
} `json:"preconditions"`
PropagationPolicy string `json:"propagationPolicy"`
}

// QueryParam can be used to manually set a URL query parameter by name.
func QueryParam(name, value string) Option {
return queryParam{
paramName: name,
paramValue: value,
}
return optionFunc(func(base string, v url.Values) string {
v.Set(name, value)
return base
})
}

type deleteOptionFunc func(r Resource, d *deleteOptions)

func (f deleteOptionFunc) updateDelete(r Resource, d *deleteOptions) { f(r, d) }
func (f deleteOptionFunc) updateURL(base string, v url.Values) string { return base }

func DeleteAtomic() Option {
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
d.Preconditions.UID = *r.GetMetadata().Uid
})
}

// DeletePropagationOrphan orphans the dependent resources during a delete.
func DeletePropagationOrphan() Option {
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
d.PropagationPolicy = "Orphan"
})
}

// DeletePropagationBackground deletes the resources and causes the garbage
// collector to delete dependent resources in the background.
func DeletePropagationBackground() Option {
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
d.PropagationPolicy = "Background"
})
}

// DeletePropagationForeground deletes the resources and causes the garbage
// collector to delete dependent resources and wait for all dependents whose
// ownerReference.blockOwnerDeletion=true. API sever will put the "foregroundDeletion"
// finalizer on the object, and sets its deletionTimestamp. This policy is
// cascading, i.e., the dependents will be deleted with Foreground.
func DeletePropagationForeground() Option {
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
d.PropagationPolicy = "Foreground"
})
}

func DeleteGracePeriod(d time.Duration) Option {
seconds := int64(d / time.Second)
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
d.GracePeriod = &seconds
})
}

// ResourceVersion causes watch operations to only show changes since
// a particular version of a resource.
func ResourceVersion(resourceVersion string) Option {
return queryParam{"resourceVersion", resourceVersion}
return QueryParam("resourceVersion", resourceVersion)
}

// Timeout declares the timeout for list and watch operations. Timeout
// is only accurate to the second.
func Timeout(d time.Duration) Option {
return queryParam{
return QueryParam(
"timeoutSeconds",
strconv.FormatInt(int64(d/time.Second), 10),
}
)
}

// Subresource is a way to interact with a part of an API object without needing
Expand All @@ -59,15 +110,9 @@ func Timeout(d time.Duration) Option {
//
// See https://kubernetes.io/docs/reference/api-concepts/
func Subresource(name string) Option {
return subresource{name}
}

type subresource struct {
name string
}

func (s subresource) updateURL(base string, v url.Values) string {
return base + "/" + s.name
return optionFunc(func(base string, v url.Values) string {
return base + "/" + name
})
}

type resourceType struct {
Expand Down

0 comments on commit f2ca415

Please sign in to comment.