Skip to content

Commit

Permalink
Support update patch options
Browse files Browse the repository at this point in the history
This supports passing update options to patch options (e.g. dry-run
options).
  • Loading branch information
DirectXMan12 committed Mar 18, 2019
1 parent b002d31 commit d17c483
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 2 deletions.
50 changes: 50 additions & 0 deletions pkg/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,27 @@ var _ = Describe("Client", func() {
PIt("should fail if the GVK cannot be mapped to a Resource", func() {

})

It("should respect passed in update options", func() {
By("creating a new client")
cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())
Expect(cl).NotTo(BeNil())

By("initially creating a Deployment")
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
Expect(err).NotTo(HaveOccurred())

By("patching the Deployment with dry-run")
err = cl.Patch(context.TODO(), dep, client.ConstantPatch(types.MergePatchType, mergePatch), client.UpdatePatchWith(client.UpdateDryRunAll()))
Expect(err).NotTo(HaveOccurred())

By("validating patched Deployment doesn't have the new annotation")
actual, err := clientset.AppsV1().Deployments(ns).Get(dep.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(actual).NotTo(BeNil())
Expect(actual.Annotations).NotTo(HaveKey("foo"))
})
})
Context("with unstructured objects", func() {
It("should patch an existing object from a go struct", func(done Done) {
Expand Down Expand Up @@ -1142,6 +1163,35 @@ var _ = Describe("Client", func() {

close(done)
})

It("should respect passed-in update options", func() {
By("creating a new client")
cl, err := client.New(cfg, client.Options{})
Expect(err).NotTo(HaveOccurred())
Expect(cl).NotTo(BeNil())

By("initially creating a Deployment")
dep, err := clientset.AppsV1().Deployments(ns).Create(dep)
Expect(err).NotTo(HaveOccurred())

By("patching the Deployment")
depName := dep.Name
u := &unstructured.Unstructured{}
scheme.Convert(dep, u, nil)
u.SetGroupVersionKind(schema.GroupVersionKind{
Group: "apps",
Kind: "Deployment",
Version: "v1",
})
err = cl.Patch(context.TODO(), u, client.ConstantPatch(types.MergePatchType, mergePatch), client.UpdatePatchWith(client.UpdateDryRunAll()))
Expect(err).NotTo(HaveOccurred())

By("validating patched Deployment does not have the new annotation")
actual, err := clientset.AppsV1().Deployments(ns).Get(depName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(actual).NotTo(BeNil())
Expect(actual.Annotations).NotTo(HaveKey("foo"))
})
})
})

Expand Down
22 changes: 22 additions & 0 deletions pkg/client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,31 @@ func UpdateDryRunAll() UpdateOptionFunc {

// PatchOptions contains options for patch requests.
type PatchOptions struct {
UpdateOptions
}

// ApplyOptions executes the given PatchOptionFuncs, mutating these PatchOptions.
// It returns the mutated PatchOptions for convenience.
func (o *PatchOptions) ApplyOptions(optFuncs []PatchOptionFunc) *PatchOptions {
for _, optFunc := range optFuncs {
optFunc(o)
}
return o
}

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

// Sadly, we need a separate function to "adapt" PatchOptions to the constituent
// update options, since there's no way to write a function that works for both.

// UpdatePatchWith adapts the given UpdateOptionFuncs to be a PatchOptionFunc.
func UpdatePatchWith(optFuncs ...UpdateOptionFunc) PatchOptionFunc {
return func(opts *PatchOptions) {
for _, optFunc := range optFuncs {
optFunc(&opts.UpdateOptions)
}
}
}
2 changes: 1 addition & 1 deletion pkg/client/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
package client

import (
"github.com/evanphx/json-patch"
jsonpatch "github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
Expand Down
2 changes: 2 additions & 0 deletions pkg/client/typed_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,12 @@ func (c *typedClient) Patch(ctx context.Context, obj runtime.Object, patch Patch
return err
}

patchOpts := &PatchOptions{}
return o.Patch(patch.Type()).
NamespaceIfScoped(o.GetNamespace(), o.isNamespaced()).
Resource(o.resource()).
Name(o.GetName()).
VersionedParams(patchOpts.ApplyOptions(opts).AsUpdateOptions(), c.paramCodec).
Body(data).
Context(ctx).
Do().
Expand Down
3 changes: 2 additions & 1 deletion pkg/client/unstructured_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ func (uc *unstructuredClient) Patch(_ context.Context, obj runtime.Object, patch
return err
}

i, err := r.Patch(u.GetName(), patch.Type(), data, metav1.UpdateOptions{})
patchOpts := &PatchOptions{}
i, err := r.Patch(u.GetName(), patch.Type(), data, *patchOpts.ApplyOptions(opts).AsUpdateOptions())
if err != nil {
return err
}
Expand Down

0 comments on commit d17c483

Please sign in to comment.