Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
dbdc434
:running: fix various typos
mcristina422 Jul 2, 2019
e0c78ca
Remove unused disableWebhookConfigInstaller flag
bradhoekstra Jul 8, 2019
2712876
Merge pull request #515 from bradhoekstra/remove-dwci
k8s-ci-robot Jul 8, 2019
4e3e16b
Merge pull request #513 from mcristina422/typos
k8s-ci-robot Jul 8, 2019
010e3c5
Only log CRD name during installation
ekuefler Jul 11, 2019
19ac705
Note on Manager#GetClient about delegating clients
DirectXMan12 Jul 17, 2019
8f633b1
Merge pull request #522 from DirectXMan12/docs/extra-manager-client-docs
k8s-ci-robot Jul 17, 2019
ad9b00b
:warning: support delete validation in validator interface
awesomenix Jul 18, 2019
3fe51b2
examples readme: documentation explaining the two provided examples
hasheddan Jul 22, 2019
8030432
examples readme: reference kubebuilder book for installation and deploy
hasheddan Jul 22, 2019
ad914c1
:sparkles: decoder now errors out when encountering an empty runtime.…
Jul 22, 2019
86ffaff
Merge pull request #529 from mengqiy/decodercheck
k8s-ci-robot Jul 23, 2019
d4d6a51
:bug: stop using hardcoded temp dir
Jul 22, 2019
72ab3fe
Merge pull request #525 from awesomenix/supportdelete
k8s-ci-robot Jul 23, 2019
fe1709a
fix(hack): Properly check if binary exists
mcristina422 Jul 23, 2019
42f8083
moving to type isnt completely necessary
mcristina422 Jul 23, 2019
68d92bb
Switch to interface-based options
DirectXMan12 Jul 24, 2019
320b6b6
Add field manager & dryrun to other opts
DirectXMan12 Jul 24, 2019
f770dcb
Merge pull request #530 from mengqiy/tmpdir
k8s-ci-robot Jul 24, 2019
7b3c83b
fix grammer of error in SetControllerReference
yashbhutwala Jul 25, 2019
5181210
Merge pull request #536 from DirectXMan12/feature/functional-options-…
k8s-ci-robot Jul 25, 2019
e2c7389
Merge pull request #539 from yashbhutwala/fix-SetControllerReference-…
k8s-ci-robot Jul 25, 2019
c49d570
Merge pull request #519 from ekuefler/patch-1
k8s-ci-robot Jul 25, 2019
59b131b
Merge pull request #527 from hasheddan/examples-readme
k8s-ci-robot Jul 25, 2019
8102998
:bug: surface controller options when using builder
awesomenix Jul 17, 2019
3b06668
✨ Implement delete collection via delete options
adracus May 26, 2019
cea57be
:bug: Preserve GroupVersionKind during Update/Patch
vincepri Jul 22, 2019
bfba246
Merge pull request #447 from adracus/feature.delete-collection
k8s-ci-robot Jul 26, 2019
ee41a80
Merge pull request #528 from vincepri/fixup-gvk
k8s-ci-robot Jul 27, 2019
37c8aa1
Return controller from builder.Build
ncdc Jul 26, 2019
402d93e
Use request Context for admission Webhook handlers
Aug 3, 2019
dc83571
Merge pull request #542 from ncdc/dynamic-watchers
k8s-ci-robot Aug 5, 2019
335a587
Merge pull request #520 from awesomenix/master
k8s-ci-robot Aug 5, 2019
e0bee34
Merge pull request #549 from seh/supply-http-request-context-to-admis…
k8s-ci-robot Aug 6, 2019
054ee41
Merge pull request #533 from mcristina422/checkInstall
k8s-ci-robot Aug 6, 2019
11818d8
:bug: fix SimpleController in builder test
Aug 6, 2019
084b73e
Merge pull request #552 from mengqiy/fixsimplecontroller
k8s-ci-robot Aug 6, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
✨ Implement delete collection via delete options
implement `CollectionOptions` in `DeleteOptions` that allow to issue a
`DeleteCollection` call under the hood

add tests + examples

Co-Authored-By: Aaron Clawson <[email protected]>
Edited-By: Solly Ross (directxman12)
  • Loading branch information
2 people authored and DirectXMan12 committed Jul 25, 2019
commit 3b0666840321713d015b479f2b16800a0fda7237
9 changes: 9 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ func (c *client) Delete(ctx context.Context, obj runtime.Object, opts ...DeleteO
return c.typedClient.Delete(ctx, obj, opts...)
}

// DeleteAllOf implements client.Client
func (c *client) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error {
_, ok := obj.(*unstructured.Unstructured)
if ok {
return c.unstructuredClient.DeleteAllOf(ctx, obj, opts...)
}
return c.typedClient.DeleteAllOf(ctx, obj, opts...)
}

// Patch implements client.Client
func (c *client) Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error {
_, ok := obj.(*unstructured.Unstructured)
Expand Down
110 changes: 109 additions & 1 deletion pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ var _ = Describe("Client", func() {
BeforeEach(func(done Done) {
atomic.AddUint64(&count, 1)
dep = &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("deployment-name-%v", count), Namespace: ns},
ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("deployment-name-%v", count), Namespace: ns, Labels: map[string]string{"app": fmt.Sprintf("bar-%v", count)}},
Spec: appsv1.DeploymentSpec{
Replicas: &replicaCount,
Selector: &metav1.LabelSelector{
Expand Down Expand Up @@ -898,6 +898,37 @@ var _ = Describe("Client", func() {
PIt("should fail if the GVK cannot be mapped to a Resource", func() {

})

It("should delete a collection of objects", func(done Done) {
cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())
Expect(cl).NotTo(BeNil())

By("initially creating two Deployments")

dep2 := dep.DeepCopy()
dep2.Name = dep2.Name + "-2"

dep, err = clientset.AppsV1().Deployments(ns).Create(dep)
Expect(err).NotTo(HaveOccurred())
dep2, err = clientset.AppsV1().Deployments(ns).Create(dep2)
Expect(err).NotTo(HaveOccurred())

depName := dep.Name
dep2Name := dep2.Name

By("deleting Deployments")
err = cl.DeleteAllOf(context.TODO(), dep, client.InNamespace(ns), client.MatchingLabels(dep.ObjectMeta.Labels))
Expect(err).NotTo(HaveOccurred())

By("validating the Deployment no longer exists")
_, err = clientset.AppsV1().Deployments(ns).Get(depName, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
_, err = clientset.AppsV1().Deployments(ns).Get(dep2Name, metav1.GetOptions{})
Expect(err).To(HaveOccurred())

close(done)
})
})
Context("with unstructured objects", func() {
It("should delete an existing object from a go struct", func(done Done) {
Expand Down Expand Up @@ -974,6 +1005,44 @@ var _ = Describe("Client", func() {

close(done)
})

It("should delete a collection of object", func(done Done) {
cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())
Expect(cl).NotTo(BeNil())

By("initially creating two Deployments")

dep2 := dep.DeepCopy()
dep2.Name = dep2.Name + "-2"

dep, err = clientset.AppsV1().Deployments(ns).Create(dep)
Expect(err).NotTo(HaveOccurred())
dep2, err = clientset.AppsV1().Deployments(ns).Create(dep2)
Expect(err).NotTo(HaveOccurred())

depName := dep.Name
dep2Name := dep2.Name

By("deleting Deployments")
u := &unstructured.Unstructured{}
Expect(scheme.Convert(dep, u, nil)).To(Succeed())
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
})
err = cl.DeleteAllOf(context.TODO(), u, client.InNamespace(ns), client.MatchingLabels(dep.ObjectMeta.Labels))
Expect(err).NotTo(HaveOccurred())

By("validating the Deployment no longer exists")
_, err = clientset.AppsV1().Deployments(ns).Get(depName, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
_, err = clientset.AppsV1().Deployments(ns).Get(dep2Name, metav1.GetOptions{})
Expect(err).To(HaveOccurred())

close(done)
})
})
})

Expand Down Expand Up @@ -1994,6 +2063,10 @@ var _ = Describe("Client", func() {
PIt("should fail if the object doesn't have meta", func() {

})

PIt("should filter results by namespace selector", func() {

})
})
})

Expand Down Expand Up @@ -2072,6 +2145,34 @@ var _ = Describe("Client", func() {
})
})

Describe("DeleteCollectionOptions", func() {
It("should be convertable to list options", func() {
gp := int64(1)
do := &client.DeleteAllOfOptions{}
do.ApplyOptions([]client.DeleteAllOfOption{
client.GracePeriodSeconds(gp),
client.MatchingLabels{"foo": "bar"},
})

listOpts := do.AsListOptions()
Expect(listOpts).NotTo(BeNil())
Expect(listOpts.LabelSelector).To(Equal("foo=bar"))
})

It("should be convertable to delete options", func() {
gp := int64(1)
do := &client.DeleteAllOfOptions{}
do.ApplyOptions([]client.DeleteAllOfOption{
client.GracePeriodSeconds(gp),
client.MatchingLabels{"foo": "bar"},
})

deleteOpts := do.AsDeleteOptions()
Expect(deleteOpts).NotTo(BeNil())
Expect(deleteOpts.GracePeriodSeconds).To(Equal(&gp))
})
})

Describe("ListOptions", func() {
It("should be convertable to metav1.ListOptions", func() {
lo := (&client.ListOptions{}).ApplyOptions([]client.ListOption{
Expand Down Expand Up @@ -2105,6 +2206,13 @@ var _ = Describe("Client", func() {
Expect(lo).NotTo(BeNil())
Expect(lo.Namespace).To(Equal("test"))
})

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

Describe("UpdateOptions", func() {
Expand Down
16 changes: 16 additions & 0 deletions pkg/client/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,22 @@ func ExampleClient_delete() {
_ = c.Delete(context.Background(), u)
}

// This example shows how to use the client with typed and unstrucurted objects to delete collections of objects.
func ExampleClient_deleteAllOf() {
// Using a typed object.
// c is a created client.
_ = c.DeleteAllOf(context.Background(), &corev1.Pod{}, client.InNamespace("foo"), client.MatchingLabels{"app": "foo"})

// Using an unstructured Object
u := &unstructured.UnstructuredList{}
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
})
_ = c.DeleteAllOf(context.Background(), u, client.InNamespace("foo"), client.MatchingLabels{"app": "foo"})
}

// This example shows how to set up and consume a field selector over a pod's volumes' secretName field.
func ExampleFieldIndexer_secretName() {
// someIndexer is a FieldIndexer over a Cache
Expand Down
39 changes: 39 additions & 0 deletions pkg/client/fake/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,49 @@ func (c *fakeClient) Delete(ctx context.Context, obj runtime.Object, opts ...cli
if err != nil {
return err
}
delOptions := client.DeleteOptions{}
delOptions.ApplyOptions(opts)

//TODO: implement propagation
return c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
}

func (c *fakeClient) DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...client.DeleteAllOfOption) error {
gvk, err := apiutil.GVKForObject(obj, scheme.Scheme)
if err != nil {
return err
}

dcOptions := client.DeleteAllOfOptions{}
dcOptions.ApplyOptions(opts)

gvr, _ := meta.UnsafeGuessKindToResource(gvk)
o, err := c.tracker.List(gvr, gvk, dcOptions.Namespace)
if err != nil {
return err
}

objs, err := meta.ExtractList(o)
if err != nil {
return err
}
filteredObjs, err := objectutil.FilterWithLabels(objs, dcOptions.LabelSelector)
if err != nil {
return err
}
for _, o := range filteredObjs {
accessor, err := meta.Accessor(o)
if err != nil {
return err
}
err = c.tracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName())
if err != nil {
return err
}
}
return nil
}

func (c *fakeClient) Update(ctx context.Context, obj runtime.Object, opts ...client.UpdateOption) error {
updateOptions := &client.UpdateOptions{}
updateOptions.ApplyOptions(opts)
Expand Down
12 changes: 12 additions & 0 deletions pkg/client/fake/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ var _ = Describe("Fake client", func() {
Expect(list.Items).To(ConsistOf(*dep2))
})

It("should be able to Delete a Collection", func() {
By("Deleting a deploymentList")
err := cl.DeleteAllOf(nil, &appsv1.Deployment{}, client.InNamespace("ns1"))
Expect(err).To(BeNil())

By("Listing all deployments in the namespace")
list := &appsv1.DeploymentList{}
err = cl.List(nil, list, client.InNamespace("ns1"))
Expect(err).To(BeNil())
Expect(list.Items).To(BeEmpty())
})

Context("with the DryRun option", func() {
It("should not create a new object", func() {
By("Creating a new configmap with DryRun")
Expand Down
3 changes: 3 additions & 0 deletions pkg/client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ type Writer interface {
// Patch patches 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.
Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOption) error

// DeleteAllOf deletes all objects of the given type matching the given options.
DeleteAllOf(ctx context.Context, obj runtime.Object, opts ...DeleteAllOfOption) error
}

// StatusClient knows how to create a client which can update status subresource
Expand Down
Loading