diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index bba17f4e..2c65af2e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -448,195 +448,195 @@ }, { "ImportPath": "k8s.io/apimachinery/pkg/api/equality", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/api/errors", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/api/meta", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/api/resource", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/apimachinery", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1alpha1", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/conversion", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/fields", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/labels", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/schema", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/selection", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/types", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/cache", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/clock", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/diff", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/errors", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/framer", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/intstr", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/json", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/net", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/runtime", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/sets", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/validation", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/validation/field", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/wait", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/util/yaml", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/version", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/pkg/watch", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect", - "Rev": "92044309870430a96dc310e4f49cf9234c4f66e4" + "Rev": "ce3cdfeed84dbf2c73db8ae4aaefd0548749e623" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", diff --git a/util/retry/BUILD b/util/retry/BUILD new file mode 100644 index 00000000..9c9bb048 --- /dev/null +++ b/util/retry/BUILD @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = ["util.go"], + deps = [ + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["util_test.go"], + library = ":go_default_library", + deps = [ + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/util/retry/OWNERS b/util/retry/OWNERS new file mode 100755 index 00000000..a4c1c2d4 --- /dev/null +++ b/util/retry/OWNERS @@ -0,0 +1,2 @@ +reviewers: +- caesarxuchao diff --git a/util/retry/util.go b/util/retry/util.go new file mode 100644 index 00000000..3ac0840a --- /dev/null +++ b/util/retry/util.go @@ -0,0 +1,79 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package retry + +import ( + "time" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/wait" +) + +// DefaultRetry is the recommended retry for a conflict where multiple clients +// are making changes to the same resource. +var DefaultRetry = wait.Backoff{ + Steps: 5, + Duration: 10 * time.Millisecond, + Factor: 1.0, + Jitter: 0.1, +} + +// DefaultBackoff is the recommended backoff for a conflict where a client +// may be attempting to make an unrelated modification to a resource under +// active management by one or more controllers. +var DefaultBackoff = wait.Backoff{ + Steps: 4, + Duration: 10 * time.Millisecond, + Factor: 5.0, + Jitter: 0.1, +} + +// RetryConflict executes the provided function repeatedly, retrying if the server returns a conflicting +// write. Callers should preserve previous executions if they wish to retry changes. It performs an +// exponential backoff. +// +// var pod *api.Pod +// err := RetryOnConflict(DefaultBackoff, func() (err error) { +// pod, err = c.Pods("mynamespace").UpdateStatus(podStatus) +// return +// }) +// if err != nil { +// // may be conflict if max retries were hit +// return err +// } +// ... +// +// TODO: Make Backoff an interface? +func RetryOnConflict(backoff wait.Backoff, fn func() error) error { + var lastConflictErr error + err := wait.ExponentialBackoff(backoff, func() (bool, error) { + err := fn() + switch { + case err == nil: + return true, nil + case errors.IsConflict(err): + lastConflictErr = err + return false, nil + default: + return false, err + } + }) + if err == wait.ErrWaitTimeout { + err = lastConflictErr + } + return err +} diff --git a/util/retry/util_test.go b/util/retry/util_test.go new file mode 100644 index 00000000..dbb4374f --- /dev/null +++ b/util/retry/util_test.go @@ -0,0 +1,71 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package retry + +import ( + "fmt" + "testing" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/wait" +) + +func TestRetryOnConflict(t *testing.T) { + opts := wait.Backoff{Factor: 1.0, Steps: 3} + conflictErr := errors.NewConflict(schema.GroupResource{Resource: "test"}, "other", nil) + + // never returns + err := RetryOnConflict(opts, func() error { + return conflictErr + }) + if err != conflictErr { + t.Errorf("unexpected error: %v", err) + } + + // returns immediately + i := 0 + err = RetryOnConflict(opts, func() error { + i++ + return nil + }) + if err != nil || i != 1 { + t.Errorf("unexpected error: %v", err) + } + + // returns immediately on error + testErr := fmt.Errorf("some other error") + err = RetryOnConflict(opts, func() error { + return testErr + }) + if err != testErr { + t.Errorf("unexpected error: %v", err) + } + + // keeps retrying + i = 0 + err = RetryOnConflict(opts, func() error { + if i < 2 { + i++ + return errors.NewConflict(schema.GroupResource{Resource: "test"}, "other", nil) + } + return nil + }) + if err != nil || i != 2 { + t.Errorf("unexpected error: %v", err) + } +}