diff --git a/go.mod b/go.mod index be1c11de..50aee99e 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( code.cloudfoundry.org/credhub-cli v0.0.0-20260316130226-db948d6c5f1c github.com/BurntSushi/toml v1.6.0 github.com/apex/log v1.9.0 - github.com/buildpacks/lifecycle v0.21.12 + github.com/buildpacks/lifecycle v0.21.13 github.com/buildpacks/pack v0.40.6 github.com/cespare/xxhash/v2 v2.3.0 github.com/google/go-containerregistry v0.21.7 diff --git a/go.sum b/go.sum index e9b1e330..913c8658 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDw github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= github.com/buildpacks/imgutil v0.0.0-20260415151438-73856e68b72b h1:wW08ZcJrzVhubQjTOkjkWzfeVHAHW0Tlmg3twecDauA= github.com/buildpacks/imgutil v0.0.0-20260415151438-73856e68b72b/go.mod h1:EBy9ZOatKkTBAWOgN2Y0aNV4P40dOdGyzzB9q2a5HBE= -github.com/buildpacks/lifecycle v0.21.12 h1:0pj2lhDTuEcArgo2fFa2LYN69ildNLzOSQgxNf1TULM= -github.com/buildpacks/lifecycle v0.21.12/go.mod h1:19rLV5zX7p68AslalkHDuNFCy2PvmipzWr1wwFcGuAg= +github.com/buildpacks/lifecycle v0.21.13 h1:Zc7b2dT/n33wpIL8MVjqGCpLDNQkUykRPoOAjDSh4Pk= +github.com/buildpacks/lifecycle v0.21.13/go.mod h1:Nlc4AwuTi26undoa+YuNmBftQHX38915+kz+kg2IZ7Q= github.com/buildpacks/pack v0.40.6 h1:w2HbQLv5UEvkbdoGt/QHmEbrMrvIwZZwuo/L0KaPdWE= github.com/buildpacks/pack v0.40.6/go.mod h1:/tcPeriACGmDZ0FCwkAyvwJaTFtDykGfaLUZk6u5aMw= github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= diff --git a/vendor/github.com/buildpacks/lifecycle/phase/exporter.go b/vendor/github.com/buildpacks/lifecycle/phase/exporter.go index db529758..9894f2a1 100644 --- a/vendor/github.com/buildpacks/lifecycle/phase/exporter.go +++ b/vendor/github.com/buildpacks/lifecycle/phase/exporter.go @@ -63,6 +63,7 @@ type LauncherConfig struct { Metadata files.LauncherMetadata } +// ExportOptions is the set of options for exporting an image. type ExportOptions struct { // WorkingImage is the image to save. WorkingImage imgutil.Image diff --git a/vendor/github.com/buildpacks/lifecycle/phase/rebaser.go b/vendor/github.com/buildpacks/lifecycle/phase/rebaser.go index 947cdf25..207b7e70 100644 --- a/vendor/github.com/buildpacks/lifecycle/phase/rebaser.go +++ b/vendor/github.com/buildpacks/lifecycle/phase/rebaser.go @@ -25,6 +25,7 @@ var ( msgUnableToSatisfyTargetConstraints = "unable to satisfy target os/arch constraints; new run image: %s, old run image: %s" ) +// Rebaser changes the underlying base image for an application image. type Rebaser struct { Logger log.Logger PlatformAPI *api.Version diff --git a/vendor/github.com/buildpacks/lifecycle/phase/retry.go b/vendor/github.com/buildpacks/lifecycle/phase/retry.go new file mode 100644 index 00000000..4dc2c6ad --- /dev/null +++ b/vendor/github.com/buildpacks/lifecycle/phase/retry.go @@ -0,0 +1,65 @@ +package phase + +import ( + stderrors "errors" + "net/http" + "time" + + "github.com/buildpacks/imgutil" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + + "github.com/buildpacks/lifecycle/log" +) + +// topLayerDelays is hardcoded array of delays +var topLayerDelays = []time.Duration{ + 100 * time.Millisecond, + 200 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + 2 * time.Second, +} + +// topLayerSleep is the function used for sleeping between retries. +// It can be replaced for testing. +var topLayerSleep = time.Sleep + +// isRetryable returns true if the error is likely transient and should be retried. +// 401 Unauthorized and 403 Forbidden are not retryable as they indicate auth/config issues. +func isRetryable(err error) bool { + if tErr, ok := stderrors.AsType[*transport.Error](err); ok { + return tErr.StatusCode != http.StatusBadRequest && + tErr.StatusCode != http.StatusUnauthorized && + tErr.StatusCode != http.StatusForbidden && + tErr.StatusCode != http.StatusMethodNotAllowed && + tErr.StatusCode != http.StatusTooManyRequests + } + return true +} + +// OpenRemoteImage opens a remote image with retry logic for registry mirror transient errors. +// go-containerregistry caches manifests, so each retry attempt creates a fresh image. +// Non-retryable errors (401, 403) are returned immediately without retry. +func OpenRemoteImage(logger log.Logger, newImage func() (imgutil.Image, error)) (imgutil.Image, error) { + var lastErr error + for attempt := 0; attempt <= len(topLayerDelays); attempt++ { + img, err := newImage() + if err == nil { + if _, err = img.TopLayer(); err == nil { + if attempt > 0 { + logger.Infof("Successfully opened remote image after %d retries", attempt) + } + return img, nil + } + } + lastErr = err + if !isRetryable(err) { + return nil, err + } + if attempt < len(topLayerDelays) { + logger.Warnf("Failed to open remote image (attempt %d/%d): %v, retrying in %v", attempt+1, len(topLayerDelays)+1, err, topLayerDelays[attempt]) + topLayerSleep(topLayerDelays[attempt]) + } + } + return nil, lastErr +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f06289be..5110f9dd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -313,7 +313,7 @@ github.com/buildpacks/imgutil/layout github.com/buildpacks/imgutil/layout/sparse github.com/buildpacks/imgutil/local github.com/buildpacks/imgutil/remote -# github.com/buildpacks/lifecycle v0.21.12 +# github.com/buildpacks/lifecycle v0.21.13 ## explicit; go 1.26.4 github.com/buildpacks/lifecycle/api github.com/buildpacks/lifecycle/archive