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

Commit 72e66cc

Browse files
committed
*: add options for setting the body of a DELETE request
1 parent c9d9831 commit 72e66cc

File tree

4 files changed

+190
-25
lines changed

4 files changed

+190
-25
lines changed

client.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,16 @@ func (c *Client) Delete(ctx context.Context, req Resource, options ...Option) er
383383
if err != nil {
384384
return err
385385
}
386-
return c.do(ctx, "DELETE", url, nil, nil)
386+
o := &deleteOptions{
387+
Kind: "DeleteOptions",
388+
APIVersion: "v1",
389+
PropagationPolicy: "Background",
390+
}
391+
for _, option := range options {
392+
option.updateDelete(req, o)
393+
}
394+
395+
return c.do(ctx, "DELETE", url, o, nil)
387396
}
388397

389398
func (c *Client) Update(ctx context.Context, req Resource, options ...Option) error {

client_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,117 @@ func TestDefaultNamespace(t *testing.T) {
213213
}
214214
}
215215

216+
func TestDelete(t *testing.T) {
217+
withNamespace(t, func(client *k8s.Client, namespace string) {
218+
cm := &corev1.ConfigMap{
219+
Metadata: &metav1.ObjectMeta{
220+
Name: k8s.String("my-configmap"),
221+
Namespace: &namespace,
222+
},
223+
}
224+
if err := client.Create(context.TODO(), cm); err != nil {
225+
t.Errorf("create configmap: %v", err)
226+
return
227+
}
228+
229+
cm2 := &corev1.ConfigMap{
230+
Metadata: &metav1.ObjectMeta{
231+
Name: k8s.String("my-configmap-2"),
232+
Namespace: &namespace,
233+
OwnerReferences: []*metav1.OwnerReference{
234+
{
235+
ApiVersion: k8s.String("v1"),
236+
Kind: k8s.String("ConfigMap"),
237+
Name: cm.Metadata.Name,
238+
Uid: cm.Metadata.Uid,
239+
BlockOwnerDeletion: k8s.Bool(true),
240+
},
241+
},
242+
},
243+
}
244+
if err := client.Create(context.TODO(), cm2); err != nil {
245+
t.Errorf("create configmap: %v", err)
246+
return
247+
}
248+
249+
if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationForeground()); err != nil {
250+
t.Fatalf("delete configmap: %v", err)
251+
}
252+
253+
var err error
254+
for i := 0; i < 50; i++ {
255+
err = func() error {
256+
err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2)
257+
if err == nil {
258+
return fmt.Errorf("expected 404 error")
259+
}
260+
apiErr, ok := err.(*k8s.APIError)
261+
if !ok {
262+
return fmt.Errorf("error was not of type APIError: %T %v", err, err)
263+
}
264+
if apiErr.Code != 404 {
265+
return fmt.Errorf("expected 404 error code, got %d", apiErr.Code)
266+
}
267+
return nil
268+
}()
269+
if err == nil {
270+
break
271+
}
272+
time.Sleep(100 * time.Millisecond)
273+
}
274+
if err != nil {
275+
t.Errorf("getting parent configmap: %v", err)
276+
}
277+
})
278+
}
279+
280+
func TestDeleteOrphan(t *testing.T) {
281+
withNamespace(t, func(client *k8s.Client, namespace string) {
282+
cm := &corev1.ConfigMap{
283+
Metadata: &metav1.ObjectMeta{
284+
Name: k8s.String("my-configmap"),
285+
Namespace: &namespace,
286+
},
287+
}
288+
if err := client.Create(context.TODO(), cm); err != nil {
289+
t.Errorf("create configmap: %v", err)
290+
return
291+
}
292+
293+
cm2 := &corev1.ConfigMap{
294+
Metadata: &metav1.ObjectMeta{
295+
Name: k8s.String("my-configmap-2"),
296+
Namespace: &namespace,
297+
OwnerReferences: []*metav1.OwnerReference{
298+
{
299+
ApiVersion: k8s.String("v1"),
300+
Kind: k8s.String("ConfigMap"),
301+
Name: cm.Metadata.Name,
302+
Uid: cm.Metadata.Uid,
303+
},
304+
},
305+
},
306+
}
307+
if err := client.Create(context.TODO(), cm2); err != nil {
308+
t.Errorf("create configmap: %v", err)
309+
return
310+
}
311+
312+
if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationOrphan()); err != nil {
313+
t.Fatalf("delete configmap: %v", err)
314+
}
315+
316+
// We're attempting to affirm a negative, that this won't eventually be deleted.
317+
// Since there's no explicit even for us to wait for
318+
time.Sleep(time.Second)
319+
320+
err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2)
321+
if err != nil {
322+
t.Errorf("failed to get configmap: %v", err)
323+
}
324+
})
325+
}
326+
216327
func Test404(t *testing.T) {
217328
withNamespace(t, func(client *k8s.Client, namespace string) {
218329
var configMap corev1.ConfigMap

labels.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type LabelSelector struct {
2929
}
3030

3131
func (l *LabelSelector) Selector() Option {
32-
return queryParam{"labelSelector", l.String()}
32+
return QueryParam("labelSelector", l.String())
3333
}
3434

3535
func (l *LabelSelector) String() string {

resource.go

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,39 +16,90 @@ import (
1616
// Option represents optional call parameters, such as label selectors.
1717
type Option interface {
1818
updateURL(base string, v url.Values) string
19+
updateDelete(r Resource, d *deleteOptions)
1920
}
2021

21-
type queryParam struct {
22-
paramName string
23-
paramValue string
24-
}
22+
type optionFunc func(base string, v url.Values) string
23+
24+
func (f optionFunc) updateDelete(r Resource, d *deleteOptions) {}
25+
func (f optionFunc) updateURL(base string, v url.Values) string { return f(base, v) }
26+
27+
type deleteOptions struct {
28+
Kind string `json:"kind"`
29+
APIVersion string `json:"apiVersion"`
2530

26-
func (o queryParam) updateURL(base string, v url.Values) string {
27-
v.Set(o.paramName, o.paramValue)
28-
return base
31+
GracePeriod *int64 `json:"gracePeriodSeconds,omitempty"`
32+
Preconditions struct {
33+
UID string `json:"uid,omitempty"`
34+
} `json:"preconditions"`
35+
PropagationPolicy string `json:"propagationPolicy"`
2936
}
3037

3138
// QueryParam can be used to manually set a URL query parameter by name.
3239
func QueryParam(name, value string) Option {
33-
return queryParam{
34-
paramName: name,
35-
paramValue: value,
36-
}
40+
return optionFunc(func(base string, v url.Values) string {
41+
v.Set(name, value)
42+
return base
43+
})
44+
}
45+
46+
type deleteOptionFunc func(r Resource, d *deleteOptions)
47+
48+
func (f deleteOptionFunc) updateDelete(r Resource, d *deleteOptions) { f(r, d) }
49+
func (f deleteOptionFunc) updateURL(base string, v url.Values) string { return base }
50+
51+
func DeleteAtomic() Option {
52+
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
53+
d.Preconditions.UID = *r.GetMetadata().Uid
54+
})
55+
}
56+
57+
// DeletePropagationOrphan orphans the dependent resources during a delete.
58+
func DeletePropagationOrphan() Option {
59+
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
60+
d.PropagationPolicy = "Orphan"
61+
})
62+
}
63+
64+
// DeletePropagationBackground deletes the resources and causes the garbage
65+
// collector to delete dependent resources in the background.
66+
func DeletePropagationBackground() Option {
67+
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
68+
d.PropagationPolicy = "Background"
69+
})
70+
}
71+
72+
// DeletePropagationForeground deletes the resources and causes the garbage
73+
// collector to delete dependent resources and wait for all dependents whose
74+
// ownerReference.blockOwnerDeletion=true. API sever will put the "foregroundDeletion"
75+
// finalizer on the object, and sets its deletionTimestamp. This policy is
76+
// cascading, i.e., the dependents will be deleted with Foreground.
77+
func DeletePropagationForeground() Option {
78+
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
79+
d.PropagationPolicy = "Foreground"
80+
})
81+
}
82+
83+
func DeleteGracePeriod(d time.Duration) Option {
84+
seconds := int64(d / time.Second)
85+
return deleteOptionFunc(func(r Resource, d *deleteOptions) {
86+
d.GracePeriod = &seconds
87+
})
3788
}
3889

3990
// ResourceVersion causes watch operations to only show changes since
4091
// a particular version of a resource.
4192
func ResourceVersion(resourceVersion string) Option {
42-
return queryParam{"resourceVersion", resourceVersion}
93+
return QueryParam("resourceVersion", resourceVersion)
4394
}
4495

4596
// Timeout declares the timeout for list and watch operations. Timeout
4697
// is only accurate to the second.
4798
func Timeout(d time.Duration) Option {
48-
return queryParam{
99+
return QueryParam(
49100
"timeoutSeconds",
50101
strconv.FormatInt(int64(d/time.Second), 10),
51-
}
102+
)
52103
}
53104

54105
// Subresource is a way to interact with a part of an API object without needing
@@ -59,15 +110,9 @@ func Timeout(d time.Duration) Option {
59110
//
60111
// See https://kubernetes.io/docs/reference/api-concepts/
61112
func Subresource(name string) Option {
62-
return subresource{name}
63-
}
64-
65-
type subresource struct {
66-
name string
67-
}
68-
69-
func (s subresource) updateURL(base string, v url.Values) string {
70-
return base + "/" + s.name
113+
return optionFunc(func(base string, v url.Values) string {
114+
return base + "/" + name
115+
})
71116
}
72117

73118
type resourceType struct {

0 commit comments

Comments
 (0)