Allow kubectl patcher to patch specific version

Give a new "ResourceVersion" option to the patch so that the patch can
be forced against a specific version. Also there is no way to customize
how many retries the patcher should do on conflicts, so also add a
"Retries" option that let's one customize it.
pull/58/head
Antoine Pelisse 2018-11-16 10:32:47 -08:00
parent 7c4d097faf
commit 89daa462ff
1 changed files with 35 additions and 1 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package apply package apply
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"strings" "strings"
@ -436,6 +437,7 @@ func (o *ApplyOptions) Run() error {
GracePeriod: o.DeleteOptions.GracePeriod, GracePeriod: o.DeleteOptions.GracePeriod,
ServerDryRun: o.ServerDryRun, ServerDryRun: o.ServerDryRun,
OpenapiSchema: openapiSchema, OpenapiSchema: openapiSchema,
Retries: maxPatchRetry,
} }
patchBytes, patchedObject, err := patcher.Patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut) patchBytes, patchedObject, err := patcher.Patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut)
@ -699,6 +701,12 @@ type Patcher struct {
GracePeriod int GracePeriod int
ServerDryRun bool ServerDryRun bool
// If set, forces the patch against a specific resourceVersion
ResourceVersion *string
// Number of retries to make if the patch fails with conflict
Retries int
OpenapiSchema openapi.Resources OpenapiSchema openapi.Resources
} }
@ -741,6 +749,22 @@ func (v *DryRunVerifier) HasSupport(gvk schema.GroupVersionKind) error {
return nil return nil
} }
func addResourceVersion(patch []byte, rv string) ([]byte, error) {
var patchMap map[string]interface{}
err := json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, err
}
u := unstructured.Unstructured{Object: patchMap}
a, err := meta.Accessor(&u)
if err != nil {
return nil, err
}
a.SetResourceVersion(rv)
return json.Marshal(patchMap)
}
func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) { func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
// Serialize the current configuration of the object from the server. // Serialize the current configuration of the object from the server.
current, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) current, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
@ -812,6 +836,13 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, names
return patch, obj, nil return patch, obj, nil
} }
if p.ResourceVersion != nil {
patch, err = addResourceVersion(patch, *p.ResourceVersion)
if err != nil {
return nil, nil, cmdutil.AddSourceToErr("Failed to insert resourceVersion in patch", source, err)
}
}
options := metav1.UpdateOptions{} options := metav1.UpdateOptions{}
if p.ServerDryRun { if p.ServerDryRun {
options.DryRun = []string{metav1.DryRunAll} options.DryRun = []string{metav1.DryRunAll}
@ -824,7 +855,10 @@ func (p *Patcher) patchSimple(obj runtime.Object, modified []byte, source, names
func (p *Patcher) Patch(current runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) { func (p *Patcher) Patch(current runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
var getErr error var getErr error
patchBytes, patchObject, err := p.patchSimple(current, modified, source, namespace, name, errOut) patchBytes, patchObject, err := p.patchSimple(current, modified, source, namespace, name, errOut)
for i := 1; i <= maxPatchRetry && errors.IsConflict(err); i++ { if p.Retries == 0 {
p.Retries = maxPatchRetry
}
for i := 1; i <= p.Retries && errors.IsConflict(err); i++ {
if i > triesBeforeBackOff { if i > triesBeforeBackOff {
p.BackOff.Sleep(backOffPeriod) p.BackOff.Sleep(backOffPeriod)
} }