diff --git a/README.md b/README.md index db8408c3be..a3e056d9b3 100644 --- a/README.md +++ b/README.md @@ -962,6 +962,16 @@ _Note: Depending on the built image, the media type of the image manifest might be either `application/vnd.oci.image.manifest.v1+json` or `application/vnd.docker.distribution.manifest.v2+json`._ +#### Flag `--push-ignore-immutable-tag-errors` + +Set this boolean flag to `true` if you want the Kaniko process to exit with +success when a push error related to tag immutability occurs. + +This is useful for example if you have parallel builds pushing the same tag +and do not care which one actually succeeds. + +Defaults to `false`. + #### Flag `--push-retry` Set this flag to the number of retries that should happen for the push of an diff --git a/cmd/executor/cmd/root.go b/cmd/executor/cmd/root.go index a3e392e755..988a4f4508 100644 --- a/cmd/executor/cmd/root.go +++ b/cmd/executor/cmd/root.go @@ -237,6 +237,7 @@ func addKanikoOptionsFlags() { RootCmd.PersistentFlags().BoolVarP(&opts.InsecurePull, "insecure-pull", "", false, "Pull from insecure registry using plain HTTP") RootCmd.PersistentFlags().BoolVarP(&opts.SkipTLSVerifyPull, "skip-tls-verify-pull", "", false, "Pull from insecure registry ignoring TLS verify") RootCmd.PersistentFlags().IntVar(&opts.PushRetry, "push-retry", 0, "Number of retries for the push operation") + RootCmd.PersistentFlags().BoolVar(&opts.PushIgnoreImmutableTagErrors, "push-ignore-immutable-tag-errors", false, "If true, known tag immutability errors are ignored and the push finishes with success.") RootCmd.PersistentFlags().IntVar(&opts.ImageFSExtractRetry, "image-fs-extract-retry", 0, "Number of retries for image FS extraction") RootCmd.PersistentFlags().IntVar(&opts.ImageDownloadRetry, "image-download-retry", 0, "Number of retries for downloading the remote image") RootCmd.PersistentFlags().StringVarP(&opts.KanikoDir, "kaniko-dir", "", constants.DefaultKanikoPath, "Path to the kaniko directory, this takes precedence over the KANIKO_DIR environment variable.") diff --git a/pkg/config/options.go b/pkg/config/options.go index 8f777f05f6..dbc1e02976 100644 --- a/pkg/config/options.go +++ b/pkg/config/options.go @@ -43,6 +43,7 @@ type RegistryOptions struct { SkipTLSVerify bool InsecurePull bool SkipTLSVerifyPull bool + PushIgnoreImmutableTagErrors bool PushRetry int ImageDownloadRetry int } diff --git a/pkg/executor/push.go b/pkg/executor/push.go index b6f19c4433..577de27710 100644 --- a/pkg/executor/push.go +++ b/pkg/executor/push.go @@ -62,6 +62,14 @@ const ( DummyDestination = "docker.io/unset-repo/unset-image-name" ) +var ( + // known tag immutability errors + errTagImmutable = []string{ + // https://cloud.google.com/artifact-registry/docs/docker/troubleshoot#push + "The repository has enabled tag immutability", + } +) + func (w *withUserAgent) RoundTrip(r *http.Request) (*http.Response, error) { ua := []string{fmt.Sprintf("kaniko/%s", version.Version())} if upstream := os.Getenv(UpstreamClientUaKey); upstream != "" { @@ -280,10 +288,23 @@ func DoPush(image v1.Image, opts *config.KanikoOptions) error { if err != nil { return err } + digest := destRef.Context().Digest(dig.String()) if err := remote.Write(destRef, image, remote.WithAuth(pushAuth), remote.WithTransport(rt)); err != nil { + if !opts.PushIgnoreImmutableTagErrors { + return err + } + + // check for known "tag immutable" errors + errStr := err.Error() + for _, candidate := range errTagImmutable { + if strings.Contains(errStr, candidate) { + logrus.Infof("Immutable tag error ignored for %s", digest) + return nil + } + } return err } - logrus.Infof("Pushed %s", destRef.Context().Digest(dig.String())) + logrus.Infof("Pushed %s", digest) return nil }