Change:
and work in this pattern: first issuing a "GET" to the API server to retrieve the latest object, then adding the label/annotation to the object, and finally issuing an "PUT" with the modified object. The request will fail if the object stored in the server has been updated between the "GET" and "PUT", even if the update has no conflict with the label/annotate request. In such cases, instead of returning an error, should retry without disturbing the end user. There are two ways to implement this: in kubectl: simply retry the label/annotate process several times if the server rejected the request because the object has been updated. The only exception is when the is set, then kubectl shouldn't retry. the strategicMergePatch instead of calling Update at client-side. PATCH is more robust because PATCH does the "get, modify, update" in the server-side, so there is less chance to have a conflict, and PATCH will retry at the server-side (see ). We need to make some adaptions though: kubectl annotate/label has the , when it's false, kubectl should retrieve the latest object to confirm there is no existing annotation/label of the same key, and then proceed with issuing the patch. if the is set, [UPDATE:] we just need to specify the resourVersion in the patch, then it will be treated as a precondition by the UpdateResource() handler in the API server. I plan to take the first approach as an immediate fix.
Immediate need is to fix the flaky e2e test AIUI.
e2e? do we have an issue out there? is an immediate fix to the flake in test-
If this is for 1.1, it needs to be P0 at this point. Should it be?
Fwiw I use annotations both from cmd line as an admin and from scripts, to experiment with loadbalancer settings. In the case of the former, I've never really had a collision, and in the case of the latter my scripts know how to retry like we retry all other updates.
Thank you When used in scripts, the annotate and label commands have collisions frequently, e.g. in the test-, see . IMO, we shouldn't require users to retry in their scripts if we know there is a simple way to avoid the collisions. Anyway, and are sent to fix this.

Discussion:
import ( "bytes" <ins> "encoding/json" </ins> "fmt" "io" "strings"

cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" <ins> "k8s.io/kubernetes/pkg/util/strategicpatch" </ins> ) // AnnotateOptions have the data required to perform the annotate operation

if err := options.Validate(args); err != nil { cmdutil.CheckErr(cmdutil.UsageError(cmd, err.Error())) } <del> if err := options.RunAnnotate(); err != nil { </del> <ins> if err := options.RunAnnotate(f); err != nil { </ins> cmdutil.CheckErr(err) } },

} // RunAnnotate does the work <del> func (o AnnotateOptions) RunAnnotate() error { </del> <ins> func (o AnnotateOptions) RunAnnotate(f *cmdutil.Factory) error { </ins> r := o.builder.Do() if err := r.Err(); err != nil { return err } return r.Visit(func(info *resource.Info, err error) error { if err != nil { return err } <del> _, uErr := cmdutil.UpdateObject(info, func(obj runtime.Object) error { err := o.updateAnnotations(obj) if err != nil { return err } return nil }) if uErr != nil { return uErr </del> <ins> name, namespace, obj := info.Name, info.Namespace, info.Object oldData, err := json.Marshal(obj) if err != nil { return err } if err := o.updateAnnotations(obj); err != nil { return err } newData, err := json.Marshal(obj) if err != nil { return err </ins> } <del> return nil </del> <ins> patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) if err != nil { return err } mapping := info.ResourceMapping() client, err := f.RESTClient(mapping) if err != nil { return err } helper := resource.NewHelper(client, mapping) _, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes) return err </ins> }) }

t.Fatalf("unexpected request: %#vn%#v", req.URL, req) return nil, nil } <del> case "PUT": </del> <ins> case "PATCH": </ins> switch req.URL.Path { case "/namespaces/test/pods/cassandra": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil

t.Fatalf("unexpected request: %#vn%#v", req.URL, req) return nil, nil } <del> case "PUT": </del> <ins> case "PATCH": </ins> switch req.URL.Path { case "/namespaces/test/pods/foo": return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil

if err := options.Validate(args); err != nil { t.Fatalf("unexpected error: %v", err) } <del> if err := options.RunAnnotate(); err != nil { </del> <ins> if err := options.RunAnnotate(f); err != nil { </ins> t.Fatalf("unexpected error: %v", err) } }