diff --git a/container/go/cmd/pusher/pusher.go b/container/go/cmd/pusher/pusher.go index 7bce6e729..02c26046a 100644 --- a/container/go/cmd/pusher/pusher.go +++ b/container/go/cmd/pusher/pusher.go @@ -44,6 +44,7 @@ var ( format = flag.String("format", "", "The format of the uploaded image (Docker or OCI).") clientConfigDir = flag.String("client-config-dir", "", "The path to the directory where the client configuration files are located. Overiddes the value from DOCKER_CONFIG.") skipUnchangedDigest = flag.Bool("skip-unchanged-digest", false, "If set to true, will only push images where the digest has changed.") + retryCount = flag.Int("retry-count", 0, "Amount of times the push will be retried. This cannot be a negative number.") layers utils.ArrayStringFlags stampInfoFile utils.ArrayStringFlags insecureRepository = flag.Bool("insecure-repository", false, "If set to true, the repository is assumed to be insecure (http vs https)") @@ -85,6 +86,9 @@ func main() { if *imgTarball == "" && *imgConfig == "" { log.Fatalln("Neither --tarball nor --config was specified.") } + if *retryCount < 0 { + log.Fatalln("-retry-count cannot be a negative number.") + } // If the user provided a client config directory, ensure it's a valid // directory and instruct the keychain resolver to use it to look for the @@ -132,8 +136,17 @@ func main() { opts = append(opts, name.Insecure) } - if err := push(stamped, img, opts...); err != nil { - log.Fatalf("Error pushing image to %s: %v", stamped, err) + for retry := 0; retry < *retryCount+1; retry++ { + err := push(stamped, img, opts...) + if err == nil { + break + } + + if *retryCount > 0 && retry < *retryCount { + log.Printf("Error pushing image to %s (attempt %d): %v", stamped, retry+1, err) + } else { + log.Fatalf("Error pushing image to %s: %v", stamped, err) + } } digestStr := "" diff --git a/container/push.bzl b/container/push.bzl index baef9c25f..22a413c28 100644 --- a/container/push.bzl +++ b/container/push.bzl @@ -90,6 +90,12 @@ def _impl(ctx): tag = tag, )) + if ctx.attr.retry_count < 0: + fail("retry_count must be a positive integer") + + if ctx.attr.retry_count > 0: + pusher_args.append("-retry-count={}".format(ctx.attr.retry_count)) + if ctx.attr.skip_unchanged_digest: pusher_args.append("-skip-unchanged-digest") if ctx.attr.insecure_repository: @@ -174,6 +180,9 @@ container_push_ = rule( allow_single_file = True, doc = "The label of the file with repository value. Overrides 'repository'.", ), + "retry_count": attr.int( + doc = "Number of times to retry pushing the image. Only positive numbers are valid.", + ), "skip_unchanged_digest": attr.bool( default = False, doc = "Check if the container registry already contain the image's digest. If yes, skip the push for that image. " + diff --git a/contrib/push-all.bzl b/contrib/push-all.bzl index c7e7f72d3..ef9ce5e8f 100644 --- a/contrib/push-all.bzl +++ b/contrib/push-all.bzl @@ -46,6 +46,8 @@ def _impl(ctx): pusher_args, pusher_inputs = _gen_img_args(ctx, image, _get_runfile_path) pusher_args += ["--stamp-info-file=%s" % _get_runfile_path(ctx, f) for f in stamp_inputs] + if ctx.attr.retry_count > 0: + pusher_args.append("--retry-count={}".format(ctx.attr.retry_count)) if ctx.attr.skip_unchanged_digest: pusher_args.append("--skip-unchanged-digest") pusher_args.append("--dst={}".format(tag)) @@ -112,6 +114,9 @@ container_push = rule( ], doc = "The form to push: Docker or OCI.", ), + "retry_count": attr.int( + doc = "Number of times to retry pushing an image", + ), "sequential": attr.bool( default = False, doc = "If true, push images sequentially.", diff --git a/docs/container.md b/docs/container.md index 326311ee4..95d464dce 100644 --- a/docs/container.md +++ b/docs/container.md @@ -221,8 +221,8 @@ please use the bazel startup flag `--loading_phase_threads=1` in your bazel invo
 container_push(name, extension, extract_config, format, image, incremental_load_template,
-               insecure_repository, registry, repository, repository_file, skip_unchanged_digest,
-               stamp, tag, tag_file, tag_tpl, windows_paths)
+               insecure_repository, registry, repository, repository_file, retry_count,
+               skip_unchanged_digest, stamp, tag, tag_file, tag_tpl, windows_paths)
 
@@ -242,6 +242,7 @@ container_push(name, registry | The registry to which we are pushing. | String | required | | | repository | The name of the image. | String | required | | | repository_file | The label of the file with repository value. Overrides 'repository'. | Label | optional | None | +| retry_count | Number of times to retry pushing the image. Only positive numbers are valid. | Integer | optional | 0 | | skip_unchanged_digest | Check if the container registry already contain the image's digest. If yes, skip the push for that image. Default to False. Note that there is no transactional guarantee between checking for digest existence and pushing the digest. This means that you should try to avoid running the same container_push targets in parallel. | Boolean | optional | False | | stamp | Whether to encode build information into the output. Possible values:

- @io_bazel_rules_docker//stamp:always: Always stamp the build information into the output, even in [--nostamp][stamp] builds. This setting should be avoided, since it potentially causes cache misses remote caching for any downstream actions that depend on it.

- @io_bazel_rules_docker//stamp:never: Always replace build information by constant values. This gives good build result caching.

- @io_bazel_rules_docker//stamp:use_stamp_flag: Embedding of build information is controlled by the [--[no]stamp][stamp] flag. Stamped binaries are not rebuilt unless their dependencies change.

[stamp]: https://docs.bazel.build/versions/main/user-manual.html#flag--stamp | Label | optional | @io_bazel_rules_docker//stamp:use_stamp_flag | | tag | The tag of the image. | String | optional | "latest" |