From d519da147b24f4bd2b425dd0f35ca6c09bd3cd60 Mon Sep 17 00:00:00 2001 From: App Platform Runtime Working Group CI Bot Date: Tue, 21 Nov 2023 18:19:50 +0000 Subject: [PATCH] Update go.mod dependencies -------- index 94768878..3c56d94d 100644 code.cloudfoundry.org/lager/v3 v3.0.2 github.com/cloudfoundry/dropsonde v1.1.0 github.com/cloudfoundry/sonde-go v0.0.0-20230911203642-fa89d986ae20 - github.com/containers/image/v5 v5.28.0 - github.com/containers/storage v1.50.2 + github.com/containers/image/v5 v5.29.0 + github.com/containers/storage v1.51.0 github.com/docker/distribution v2.8.3+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-units v0.5.0 --- go.mod | 4 +- go.sum | 22 +- .../image/v5/docker/distribution_error.go | 3 +- .../image/v5/docker/docker_client.go | 5 + .../image/v5/docker/docker_image_dest.go | 65 ++-- .../containers/image/v5/docker/errors.go | 2 +- .../containers/image/v5/image/unparsed.go | 22 ++ .../image/v5/internal/blobinfocache/types.go | 9 +- .../containers/image/v5/internal/image/oci.go | 57 ++-- .../internal/imagedestination/impl/helpers.go | 5 + .../internal/manifest/docker_schema2_list.go | 4 +- .../image/v5/internal/manifest/oci_index.go | 6 +- .../v5/internal/unparsedimage/wrapper.go | 38 +++ .../image/v5/oci/layout/oci_delete.go | 240 +++++++++++++++ .../image/v5/oci/layout/oci_dest.go | 15 +- .../containers/image/v5/oci/layout/oci_src.go | 2 +- .../image/v5/oci/layout/oci_transport.go | 50 ++-- .../image/v5/pkg/docker/config/config.go | 281 +++++++++++++----- .../v5/pkg/tlsclientconfig/tlsclientconfig.go | 2 +- .../containers/image/v5/types/types.go | 8 +- .../containers/image/v5/version/version.go | 2 +- .../containers/storage/pkg/system/rm.go | 6 +- .../storage/pkg/unshare/unshare_darwin.go | 5 + .../storage/pkg/unshare/unshare_linux.go | 10 + .../pkg/unshare/unshare_unsupported.go | 5 + vendor/modules.txt | 5 +- 26 files changed, 698 insertions(+), 175 deletions(-) create mode 100644 vendor/github.com/containers/image/v5/internal/unparsedimage/wrapper.go create mode 100644 vendor/github.com/containers/image/v5/oci/layout/oci_delete.go diff --git a/go.mod b/go.mod index 947688789..3c56d94dc 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,8 @@ require ( code.cloudfoundry.org/lager/v3 v3.0.2 github.com/cloudfoundry/dropsonde v1.1.0 github.com/cloudfoundry/sonde-go v0.0.0-20230911203642-fa89d986ae20 - github.com/containers/image/v5 v5.28.0 - github.com/containers/storage v1.50.2 + github.com/containers/image/v5 v5.29.0 + github.com/containers/storage v1.51.0 github.com/docker/distribution v2.8.3+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-units v0.5.0 diff --git a/go.sum b/go.sum index c2b900a7a..ef8dad79c 100644 --- a/go.sum +++ b/go.sum @@ -2,21 +2,24 @@ code.cloudfoundry.org/commandrunner v0.0.0-20230612151827-2b11a2b4e9b8 h1:RajW6z code.cloudfoundry.org/commandrunner v0.0.0-20230612151827-2b11a2b4e9b8/go.mod h1:B4Pih80MJdiTapEig6xvghCwosbFX4+s1Qm9KJSbxCM= code.cloudfoundry.org/lager/v3 v3.0.2 h1:H0dcQY+814G1Ea0e5K/AMaMpcr+Pe5Iv+AALJEwrP9U= code.cloudfoundry.org/lager/v3 v3.0.2/go.mod h1:zA6tOIWhr5uZUez+PGpdfBHDWQOfhOrr0cgKDagZPwk= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/apoydence/eachers v0.0.0-20181020210610-23942921fe77 h1:afT88tB6u9JCKQZVAAaa9ICz/uGn5Uw9ekn6P22mYKM= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cloudfoundry/dropsonde v1.1.0 h1:nerhj8K0heOsv/U/Ddou5Esw56YlNeHHJH6MP9QlACQ= github.com/cloudfoundry/dropsonde v1.1.0/go.mod h1:OrkxsBrAvM8X0Ve9vaSNKLR+/Jeohu3+J0M4JEaTmnM= github.com/cloudfoundry/sonde-go v0.0.0-20230911203642-fa89d986ae20 h1:mlqnfrDRkWkO6HQ9CT0KkBZy5co/CgTX3xSmkuUNXHs= github.com/cloudfoundry/sonde-go v0.0.0-20230911203642-fa89d986ae20/go.mod h1:GiUWdcLKuM+RX4gSQBSHn3ADveMrRadWxuY6AFK5JNk= -github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt9g048= -github.com/containers/image/v5 v5.28.0/go.mod h1:9aPnNkwHNHgGl9VlQxXEshvmOJRbdRAc1rNDD6sP2eU= +github.com/containers/image/v5 v5.29.0 h1:9+nhS/ZM7c4Kuzu5tJ0NMpxrgoryOJ2HAYTgG8Ny7j4= +github.com/containers/image/v5 v5.29.0/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.1.9 h1:2Csfba4jse85Raxk5HIyEk8OwZNjRvfkhEGijOjIdEM= github.com/containers/ocicrypt v1.1.9/go.mod h1:dTKx1918d8TDkxXvarscpNVY+lyPakPNFN4jwA9GBys= -github.com/containers/storage v1.50.2 h1:Fys4BjFUVNRBEXlO70hFI48VW4EXsgnGisTpk9tTMsE= -github.com/containers/storage v1.50.2/go.mod h1:dpspZsUrcKD8SpTofvKWhwPDHD0MkO4Q7VE+oYdWkiA= +github.com/containers/storage v1.51.0 h1:AowbcpiWXzAjHosKz7MKvPEqpyX+ryZA/ZurytRrFNA= +github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,6 +27,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= @@ -32,6 +36,7 @@ github.com/docker/docker-credential-helpers v0.8.0 h1:YQFtbBQb4VrpoPxhFuzEBPQ9E1 github.com/docker/docker-credential-helpers v0.8.0/go.mod h1:UGFXcuoQ5TxPiB54nHOZ32AWRqQdECoh/Mg0AlEYb40= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= @@ -63,13 +68,16 @@ github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g= github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= @@ -88,10 +96,15 @@ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bl github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -120,6 +133,7 @@ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUU golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= diff --git a/vendor/github.com/containers/image/v5/docker/distribution_error.go b/vendor/github.com/containers/image/v5/docker/distribution_error.go index 0fe915249..11b42c6e0 100644 --- a/vendor/github.com/containers/image/v5/docker/distribution_error.go +++ b/vendor/github.com/containers/image/v5/docker/distribution_error.go @@ -24,6 +24,7 @@ import ( "github.com/docker/distribution/registry/api/errcode" dockerChallenge "github.com/docker/distribution/registry/client/auth/challenge" + "golang.org/x/exp/slices" ) // errNoErrorsInBody is returned when an HTTP response body parses to an empty @@ -105,7 +106,7 @@ func makeErrorList(err error) []error { } func mergeErrors(err1, err2 error) error { - return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...)) + return errcode.Errors(append(slices.Clone(makeErrorList(err1)), makeErrorList(err2)...)) } // handleErrorResponse returns error parsed from HTTP response for an diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go index 288dd1a93..6ce8f7008 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -363,6 +363,11 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima hostname := registry if registry == dockerHostname { hostname = dockerV1Hostname + // A search term of library/foo does not find the library/foo image on the docker.io servers, + // which is surprising - and that Docker is modifying the search term client-side this same way, + // and it seems convenient to do the same thing. + // Read more here: https://github.com/containers/image/pull/2133#issue-1928524334 + image = strings.TrimPrefix(image, "library/") } client, err := newDockerClient(sys, hostname, registry) diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go index 0e7b154cc..774068c27 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go @@ -137,7 +137,7 @@ func (d *dockerImageDestination) PutBlobWithOptions(ctx context.Context, stream // If requested, precompute the blob digest to prevent uploading layers that already exist on the registry. // This functionality is particularly useful when BlobInfoCache has not been populated with compressed digests, // the source blob is uncompressed, and the destination blob is being compressed "on the fly". - if inputInfo.Digest == "" && d.c.sys.DockerRegistryPushPrecomputeDigests { + if inputInfo.Digest == "" && d.c.sys != nil && d.c.sys.DockerRegistryPushPrecomputeDigests { logrus.Debugf("Precomputing digest layer for %s", reference.Path(d.ref.ref)) streamCopy, cleanup, err := streamdigest.ComputeBlobInfo(d.c.sys, stream, &inputInfo) if err != nil { @@ -341,39 +341,58 @@ func (d *dockerImageDestination) TryReusingBlobWithOptions(ctx context.Context, // Then try reusing blobs from other locations. candidates := options.Cache.CandidateLocations2(d.ref.Transport(), bicTransportScope(d.ref), info.Digest, options.CanSubstitute) for _, candidate := range candidates { - candidateRepo, err := parseBICLocationReference(candidate.Location) - if err != nil { - logrus.Debugf("Error parsing BlobInfoCache location reference: %s", err) - continue - } + var err error compressionOperation, compressionAlgorithm, err := blobinfocache.OperationAndAlgorithmForCompressor(candidate.CompressorName) if err != nil { logrus.Debugf("OperationAndAlgorithmForCompressor Failed: %v", err) continue } + var candidateRepo reference.Named + if !candidate.UnknownLocation { + candidateRepo, err = parseBICLocationReference(candidate.Location) + if err != nil { + logrus.Debugf("Error parsing BlobInfoCache location reference: %s", err) + continue + } + } if !impl.BlobMatchesRequiredCompression(options, compressionAlgorithm) { requiredCompression := "nil" if compressionAlgorithm != nil { requiredCompression = compressionAlgorithm.Name() } - logrus.Debugf("Ignoring candidate blob %s as reuse candidate due to compression mismatch ( %s vs %s ) in %s", candidate.Digest.String(), options.RequiredCompression.Name(), requiredCompression, candidateRepo.Name()) + if !candidate.UnknownLocation { + logrus.Debugf("Ignoring candidate blob %s as reuse candidate due to compression mismatch ( %s vs %s ) in %s", candidate.Digest.String(), options.RequiredCompression.Name(), requiredCompression, candidateRepo.Name()) + } else { + logrus.Debugf("Ignoring candidate blob %s as reuse candidate due to compression mismatch ( %s vs %s ) with no location match, checking current repo", candidate.Digest.String(), options.RequiredCompression.Name(), requiredCompression) + } continue } - if candidate.CompressorName != blobinfocache.Uncompressed { - logrus.Debugf("Trying to reuse cached location %s compressed with %s in %s", candidate.Digest.String(), candidate.CompressorName, candidateRepo.Name()) + if !candidate.UnknownLocation { + if candidate.CompressorName != blobinfocache.Uncompressed { + logrus.Debugf("Trying to reuse blob with cached digest %s compressed with %s in destination repo %s", candidate.Digest.String(), candidate.CompressorName, candidateRepo.Name()) + } else { + logrus.Debugf("Trying to reuse blob with cached digest %s in destination repo %s", candidate.Digest.String(), candidateRepo.Name()) + } + // Sanity checks: + if reference.Domain(candidateRepo) != reference.Domain(d.ref.ref) { + // OCI distribution spec 1.1 allows mounting blobs without specifying the source repo + // (the "from" parameter); in that case we might try to use these candidates as well. + // + // OTOH that would mean we can’t do the “blobExists” check, and if there is no match + // we could get an upload request that we would have to cancel. + logrus.Debugf("... Internal error: domain %s does not match destination %s", reference.Domain(candidateRepo), reference.Domain(d.ref.ref)) + continue + } } else { - logrus.Debugf("Trying to reuse cached location %s with no compression in %s", candidate.Digest.String(), candidateRepo.Name()) - } - - // Sanity checks: - if reference.Domain(candidateRepo) != reference.Domain(d.ref.ref) { - // OCI distribution spec 1.1 allows mounting blobs without specifying the source repo - // (the "from" parameter); in that case we might try to use these candidates as well. - // - // OTOH that would mean we can’t do the “blobExists” check, and if there is no match - // we could get an upload request that we would have to cancel. - logrus.Debugf("... Internal error: domain %s does not match destination %s", reference.Domain(candidateRepo), reference.Domain(d.ref.ref)) - continue + if candidate.CompressorName != blobinfocache.Uncompressed { + logrus.Debugf("Trying to reuse blob with cached digest %s compressed with %s with no location match, checking current repo", candidate.Digest.String(), candidate.CompressorName) + } else { + logrus.Debugf("Trying to reuse blob with cached digest %s in destination repo with no location match, checking current repo", candidate.Digest.String()) + } + // This digest is a known variant of this blob but we don’t + // have a recorded location in this registry, let’s try looking + // for it in the current repo. + candidateRepo = reference.TrimNamed(d.ref.ref) } if candidateRepo.Name() == d.ref.ref.Name() && candidate.Digest == info.Digest { logrus.Debug("... Already tried the primary destination") @@ -688,6 +707,10 @@ func (d *dockerImageDestination) putSignaturesToSigstoreAttachments(ctx context. } } + // To make sure we can safely append to the slices of ociManifest, without adding a remote dependency on the code that creates it. + ociManifest.Layers = slices.Clone(ociManifest.Layers) + // We don’t need to ^^^ for ociConfig.RootFS.DiffIDs because we have created it empty ourselves, and json.Unmarshal is documented to append() to + // the slice in the original object (or in a newly allocated object). for _, sig := range signatures { mimeType := sig.UntrustedMIMEType() payloadBlob := sig.UntrustedPayload() diff --git a/vendor/github.com/containers/image/v5/docker/errors.go b/vendor/github.com/containers/image/v5/docker/errors.go index e03969189..4392f9d18 100644 --- a/vendor/github.com/containers/image/v5/docker/errors.go +++ b/vendor/github.com/containers/image/v5/docker/errors.go @@ -88,7 +88,7 @@ func registryHTTPResponseToError(res *http.Response) error { response = response[:50] + "..." } // %.0w makes e visible to error.Unwrap() without including any text - err = fmt.Errorf("StatusCode: %d, %s%.0w", e.StatusCode, response, e) + err = fmt.Errorf("StatusCode: %d, %q%.0w", e.StatusCode, response, e) case errcode.Error: // e.Error() is fmt.Sprintf("%s: %s", e.Code.Error(), e.Message, which is usually // rather redundant. So reword it without using e.Code.Error() if e.Message is the default. diff --git a/vendor/github.com/containers/image/v5/image/unparsed.go b/vendor/github.com/containers/image/v5/image/unparsed.go index 123f6ce6f..f2ebb929a 100644 --- a/vendor/github.com/containers/image/v5/image/unparsed.go +++ b/vendor/github.com/containers/image/v5/image/unparsed.go @@ -2,6 +2,8 @@ package image import ( "github.com/containers/image/v5/internal/image" + "github.com/containers/image/v5/internal/private" + "github.com/containers/image/v5/internal/unparsedimage" "github.com/containers/image/v5/types" "github.com/opencontainers/go-digest" ) @@ -17,3 +19,23 @@ type UnparsedImage = image.UnparsedImage func UnparsedInstance(src types.ImageSource, instanceDigest *digest.Digest) *UnparsedImage { return image.UnparsedInstance(src, instanceDigest) } + +// unparsedWithRef wraps a private.UnparsedImage, claiming another replacementRef +type unparsedWithRef struct { + private.UnparsedImage + ref types.ImageReference +} + +func (uwr *unparsedWithRef) Reference() types.ImageReference { + return uwr.ref +} + +// UnparsedInstanceWithReference returns a types.UnparsedImage for wrappedInstance which claims to be a replacementRef. +// This is useful for combining image data with other reference values, e.g. to check signatures on a locally-pulled image +// based on a remote-registry policy. +func UnparsedInstanceWithReference(wrappedInstance types.UnparsedImage, replacementRef types.ImageReference) types.UnparsedImage { + return &unparsedWithRef{ + UnparsedImage: unparsedimage.FromPublic(wrappedInstance), + ref: replacementRef, + } +} diff --git a/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go b/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go index fdd245812..429d68263 100644 --- a/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go +++ b/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go @@ -32,7 +32,7 @@ type BlobInfoCache2 interface { // otherwise the cache could be poisoned and cause us to make incorrect edits to type // information in a manifest. RecordDigestCompressorName(anyDigest digest.Digest, compressorName string) - // CandidateLocations2 returns a prioritized, limited, number of blobs and their locations + // CandidateLocations2 returns a prioritized, limited, number of blobs and their locations (if known) // that could possibly be reused within the specified (transport scope) (if they still // exist, which is not guaranteed). // @@ -46,7 +46,8 @@ type BlobInfoCache2 interface { // BICReplacementCandidate2 is an item returned by BlobInfoCache2.CandidateLocations2. type BICReplacementCandidate2 struct { - Digest digest.Digest - CompressorName string // either the Name() of a known pkg/compression.Algorithm, or Uncompressed or UnknownCompression - Location types.BICLocationReference + Digest digest.Digest + CompressorName string // either the Name() of a known pkg/compression.Algorithm, or Uncompressed or UnknownCompression + UnknownLocation bool // is true when `Location` for this blob is not set + Location types.BICLocationReference // not set if UnknownLocation is set to `true` } diff --git a/vendor/github.com/containers/image/v5/internal/image/oci.go b/vendor/github.com/containers/image/v5/internal/image/oci.go index 6629967be..df0e8e417 100644 --- a/vendor/github.com/containers/image/v5/internal/image/oci.go +++ b/vendor/github.com/containers/image/v5/internal/image/oci.go @@ -196,14 +196,12 @@ func (m *manifestOCI1) convertToManifestSchema2Generic(ctx context.Context, opti return m.convertToManifestSchema2(ctx, options) } -// prepareLayerDecryptEditsIfNecessary checks if options requires layer decryptions. +// layerEditsOfOCIOnlyFeatures checks if options requires some layer edits to be done before converting to a Docker format. // If not, it returns (nil, nil). // If decryption is required, it returns a set of edits to provide to OCI1.UpdateLayerInfos, // and edits *options to not try decryption again. -func (m *manifestOCI1) prepareLayerDecryptEditsIfNecessary(options *types.ManifestUpdateOptions) ([]types.BlobInfo, error) { - if options == nil || !slices.ContainsFunc(options.LayerInfos, func(info types.BlobInfo) bool { - return info.CryptoOperation == types.Decrypt - }) { +func (m *manifestOCI1) layerEditsOfOCIOnlyFeatures(options *types.ManifestUpdateOptions) ([]types.BlobInfo, error) { + if options == nil || options.LayerInfos == nil { return nil, nil } @@ -212,19 +210,35 @@ func (m *manifestOCI1) prepareLayerDecryptEditsIfNecessary(options *types.Manife return nil, fmt.Errorf("preparing to decrypt before conversion: %d layers vs. %d layer edits", len(originalInfos), len(options.LayerInfos)) } - res := slices.Clone(originalInfos) // Start with a full copy so that we don't forget to copy anything: use the current data in full unless we intentionaly deviate. - updatedEdits := slices.Clone(options.LayerInfos) - for i, info := range options.LayerInfos { - if info.CryptoOperation == types.Decrypt { - res[i].CryptoOperation = types.Decrypt - updatedEdits[i].CryptoOperation = types.PreserveOriginalCrypto // Don't try to decrypt in a schema[12] manifest later, that would fail. + ociOnlyEdits := slices.Clone(originalInfos) // Start with a full copy so that we don't forget to copy anything: use the current data in full unless we intentionally deviate. + laterEdits := slices.Clone(options.LayerInfos) + needsOCIOnlyEdits := false + for i, edit := range options.LayerInfos { + // Unless determined otherwise, don't do any compression-related MIME type conversions. m.LayerInfos() should not set these edit instructions, but be explicit. + ociOnlyEdits[i].CompressionOperation = types.PreserveOriginal + ociOnlyEdits[i].CompressionAlgorithm = nil + + if edit.CryptoOperation == types.Decrypt { + needsOCIOnlyEdits = true // Encrypted types must be removed before conversion because they can’t be represented in Docker schemas + ociOnlyEdits[i].CryptoOperation = types.Decrypt + laterEdits[i].CryptoOperation = types.PreserveOriginalCrypto // Don't try to decrypt in a schema[12] manifest later, that would fail. + } + + if originalInfos[i].MediaType == imgspecv1.MediaTypeImageLayerZstd || + originalInfos[i].MediaType == imgspecv1.MediaTypeImageLayerNonDistributableZstd { //nolint:staticcheck // NonDistributable layers are deprecated, but we want to continue to support manipulating pre-existing images. + needsOCIOnlyEdits = true // Zstd MIME types must be removed before conversion because they can’t be represented in Docker schemas. + ociOnlyEdits[i].CompressionOperation = edit.CompressionOperation + ociOnlyEdits[i].CompressionAlgorithm = edit.CompressionAlgorithm + laterEdits[i].CompressionOperation = types.PreserveOriginal + laterEdits[i].CompressionAlgorithm = nil } - // Don't do any compression-related MIME type conversions. m.LayerInfos() should not set these edit instructions, but be explicit. - res[i].CompressionOperation = types.PreserveOriginal - res[i].CompressionAlgorithm = nil } - options.LayerInfos = updatedEdits - return res, nil + if !needsOCIOnlyEdits { + return nil, nil + } + + options.LayerInfos = laterEdits + return ociOnlyEdits, nil } // convertToManifestSchema2 returns a genericManifest implementation converted to manifest.DockerV2Schema2MediaType. @@ -238,15 +252,15 @@ func (m *manifestOCI1) convertToManifestSchema2(_ context.Context, options *type // Mostly we first make a format conversion, and _afterwards_ do layer edits. But first we need to do the layer edits // which remove OCI-specific features, because trying to convert those layers would fail. - // So, do the layer updates for decryption. + // So, do the layer updates for decryption, and for conversions from Zstd. ociManifest := m.m - layerDecryptEdits, err := m.prepareLayerDecryptEditsIfNecessary(options) + ociOnlyEdits, err := m.layerEditsOfOCIOnlyFeatures(options) if err != nil { return nil, err } - if layerDecryptEdits != nil { + if ociOnlyEdits != nil { ociManifest = manifest.OCI1Clone(ociManifest) - if err := ociManifest.UpdateLayerInfos(layerDecryptEdits); err != nil { + if err := ociManifest.UpdateLayerInfos(ociOnlyEdits); err != nil { return nil, err } } @@ -275,9 +289,8 @@ func (m *manifestOCI1) convertToManifestSchema2(_ context.Context, options *type layers[idx].MediaType = manifest.DockerV2Schema2LayerMediaType case imgspecv1.MediaTypeImageLayerZstd: return nil, fmt.Errorf("Error during manifest conversion: %q: zstd compression is not supported for docker images", layers[idx].MediaType) - // FIXME: s/Zsdt/Zstd/ after ocicrypt with https://github.com/containers/ocicrypt/pull/91 is released case ociencspec.MediaTypeLayerEnc, ociencspec.MediaTypeLayerGzipEnc, ociencspec.MediaTypeLayerZstdEnc, - ociencspec.MediaTypeLayerNonDistributableEnc, ociencspec.MediaTypeLayerNonDistributableGzipEnc, ociencspec.MediaTypeLayerNonDistributableZsdtEnc: + ociencspec.MediaTypeLayerNonDistributableEnc, ociencspec.MediaTypeLayerNonDistributableGzipEnc, ociencspec.MediaTypeLayerNonDistributableZstdEnc: return nil, fmt.Errorf("during manifest conversion: encrypted layers (%q) are not supported in docker images", layers[idx].MediaType) default: return nil, fmt.Errorf("Unknown media type during manifest conversion: %q", layers[idx].MediaType) diff --git a/vendor/github.com/containers/image/v5/internal/imagedestination/impl/helpers.go b/vendor/github.com/containers/image/v5/internal/imagedestination/impl/helpers.go index d5de81a61..5d28b3e73 100644 --- a/vendor/github.com/containers/image/v5/internal/imagedestination/impl/helpers.go +++ b/vendor/github.com/containers/image/v5/internal/imagedestination/impl/helpers.go @@ -12,6 +12,11 @@ func BlobMatchesRequiredCompression(options private.TryReusingBlobOptions, candi if options.RequiredCompression == nil { return true // no requirement imposed } + if options.RequiredCompression.Name() == compression.ZstdChunkedAlgorithmName { + // HACK: Never match when the caller asks for zstd:chunked, because we don’t record the annotations required to use the chunked blobs. + // The caller must re-compress to build those annotations. + return false + } return candidateCompression != nil && (options.RequiredCompression.Name() == candidateCompression.Name()) } diff --git a/vendor/github.com/containers/image/v5/internal/manifest/docker_schema2_list.go b/vendor/github.com/containers/image/v5/internal/manifest/docker_schema2_list.go index 357e2f3d3..7ce5bb069 100644 --- a/vendor/github.com/containers/image/v5/internal/manifest/docker_schema2_list.go +++ b/vendor/github.com/containers/image/v5/internal/manifest/docker_schema2_list.go @@ -133,7 +133,9 @@ func (index *Schema2ListPublic) editInstances(editInstances []ListEdit) error { } } if len(addedEntries) != 0 { - index.Manifests = append(index.Manifests, addedEntries...) + // slices.Clone() here to ensure a private backing array; + // an external caller could have manually created Schema2ListPublic with a slice with extra capacity. + index.Manifests = append(slices.Clone(index.Manifests), addedEntries...) } return nil } diff --git a/vendor/github.com/containers/image/v5/internal/manifest/oci_index.go b/vendor/github.com/containers/image/v5/internal/manifest/oci_index.go index dcd2646d1..d8d06513b 100644 --- a/vendor/github.com/containers/image/v5/internal/manifest/oci_index.go +++ b/vendor/github.com/containers/image/v5/internal/manifest/oci_index.go @@ -167,7 +167,9 @@ func (index *OCI1IndexPublic) editInstances(editInstances []ListEdit) error { } } if len(addedEntries) != 0 { - index.Manifests = append(index.Manifests, addedEntries...) + // slices.Clone() here to ensure the slice uses a private backing array; + // an external caller could have manually created OCI1IndexPublic with a slice with extra capacity. + index.Manifests = append(slices.Clone(index.Manifests), addedEntries...) } if len(addedEntries) != 0 || updatedAnnotations { slices.SortStableFunc(index.Manifests, func(a, b imgspecv1.Descriptor) int { @@ -220,7 +222,7 @@ func (ic instanceCandidate) isPreferredOver(other *instanceCandidate, preferGzip case ic.manifestPosition != other.manifestPosition: return ic.manifestPosition < other.manifestPosition } - panic("internal error: invalid comparision between two candidates") // This should not be reachable because in all calls we make, the two candidates differ at least in manifestPosition. + panic("internal error: invalid comparison between two candidates") // This should not be reachable because in all calls we make, the two candidates differ at least in manifestPosition. } // chooseInstance is a private equivalent to ChooseInstanceByCompression, diff --git a/vendor/github.com/containers/image/v5/internal/unparsedimage/wrapper.go b/vendor/github.com/containers/image/v5/internal/unparsedimage/wrapper.go new file mode 100644 index 000000000..fe65b1a98 --- /dev/null +++ b/vendor/github.com/containers/image/v5/internal/unparsedimage/wrapper.go @@ -0,0 +1,38 @@ +package unparsedimage + +import ( + "context" + + "github.com/containers/image/v5/internal/private" + "github.com/containers/image/v5/internal/signature" + "github.com/containers/image/v5/types" +) + +// wrapped provides the private.UnparsedImage operations +// for an object that only implements types.UnparsedImage +type wrapped struct { + types.UnparsedImage +} + +// FromPublic(unparsed) returns an object that provides the private.UnparsedImage API +func FromPublic(unparsed types.UnparsedImage) private.UnparsedImage { + if unparsed2, ok := unparsed.(private.UnparsedImage); ok { + return unparsed2 + } + return &wrapped{ + UnparsedImage: unparsed, + } +} + +// UntrustedSignatures is like ImageSource.GetSignaturesWithFormat, but the result is cached; it is OK to call this however often you need. +func (w *wrapped) UntrustedSignatures(ctx context.Context) ([]signature.Signature, error) { + sigs, err := w.Signatures(ctx) + if err != nil { + return nil, err + } + res := []signature.Signature{} + for _, sig := range sigs { + res = append(res, signature.SimpleSigningFromBlob(sig)) + } + return res, nil +} diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_delete.go b/vendor/github.com/containers/image/v5/oci/layout/oci_delete.go new file mode 100644 index 000000000..8dd54f255 --- /dev/null +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_delete.go @@ -0,0 +1,240 @@ +package layout + +import ( + "context" + "encoding/json" + "fmt" + "io/fs" + "os" + + "github.com/containers/image/v5/internal/set" + "github.com/containers/image/v5/types" + digest "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/sirupsen/logrus" + "golang.org/x/exp/slices" +) + +// DeleteImage deletes the named image from the directory, if supported. +func (ref ociReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error { + sharedBlobsDir := "" + if sys != nil && sys.OCISharedBlobDirPath != "" { + sharedBlobsDir = sys.OCISharedBlobDirPath + } + + descriptor, descriptorIndex, err := ref.getManifestDescriptor() + if err != nil { + return err + } + + var blobsUsedByImage map[digest.Digest]int + + switch descriptor.MediaType { + case imgspecv1.MediaTypeImageManifest: + blobsUsedByImage, err = ref.getBlobsUsedInSingleImage(&descriptor, sharedBlobsDir) + case imgspecv1.MediaTypeImageIndex: + blobsUsedByImage, err = ref.getBlobsUsedInImageIndex(&descriptor, sharedBlobsDir) + default: + return fmt.Errorf("unsupported mediaType in index: %q", descriptor.MediaType) + } + if err != nil { + return err + } + + blobsToDelete, err := ref.getBlobsToDelete(blobsUsedByImage, sharedBlobsDir) + if err != nil { + return err + } + + err = ref.deleteBlobs(blobsToDelete) + if err != nil { + return err + } + + return ref.deleteReferenceFromIndex(descriptorIndex) +} + +func (ref ociReference) getBlobsUsedInSingleImage(descriptor *imgspecv1.Descriptor, sharedBlobsDir string) (map[digest.Digest]int, error) { + manifest, err := ref.getManifest(descriptor, sharedBlobsDir) + if err != nil { + return nil, err + } + blobsUsedInManifest := ref.getBlobsUsedInManifest(manifest) + blobsUsedInManifest[descriptor.Digest]++ // Add the current manifest to the list of blobs used by this reference + + return blobsUsedInManifest, nil +} + +func (ref ociReference) getBlobsUsedInImageIndex(descriptor *imgspecv1.Descriptor, sharedBlobsDir string) (map[digest.Digest]int, error) { + blobPath, err := ref.blobPath(descriptor.Digest, sharedBlobsDir) + if err != nil { + return nil, err + } + index, err := parseIndex(blobPath) + if err != nil { + return nil, err + } + + blobsUsedInImageRefIndex := make(map[digest.Digest]int) + err = ref.addBlobsUsedInIndex(blobsUsedInImageRefIndex, index, sharedBlobsDir) + if err != nil { + return nil, err + } + blobsUsedInImageRefIndex[descriptor.Digest]++ // Add the nested index in the list of blobs used by this reference + + return blobsUsedInImageRefIndex, nil +} + +// Updates a map of digest with the usage count, so a blob that is referenced three times will have 3 in the map +func (ref ociReference) addBlobsUsedInIndex(destination map[digest.Digest]int, index *imgspecv1.Index, sharedBlobsDir string) error { + for _, descriptor := range index.Manifests { + destination[descriptor.Digest]++ + switch descriptor.MediaType { + case imgspecv1.MediaTypeImageManifest: + manifest, err := ref.getManifest(&descriptor, sharedBlobsDir) + if err != nil { + return err + } + for digest, count := range ref.getBlobsUsedInManifest(manifest) { + destination[digest] += count + } + case imgspecv1.MediaTypeImageIndex: + blobPath, err := ref.blobPath(descriptor.Digest, sharedBlobsDir) + if err != nil { + return err + } + index, err := parseIndex(blobPath) + if err != nil { + return err + } + err = ref.addBlobsUsedInIndex(destination, index, sharedBlobsDir) + if err != nil { + return err + } + default: + return fmt.Errorf("unsupported mediaType in index: %q", descriptor.MediaType) + } + } + + return nil +} + +func (ref ociReference) getBlobsUsedInManifest(manifest *imgspecv1.Manifest) map[digest.Digest]int { + blobsUsedInManifest := make(map[digest.Digest]int, 0) + + blobsUsedInManifest[manifest.Config.Digest]++ + for _, layer := range manifest.Layers { + blobsUsedInManifest[layer.Digest]++ + } + + return blobsUsedInManifest +} + +// This takes in a map of the digest and their usage count in the manifest to be deleted +// It will compare it to the digest usage in the root index, and return a set of the blobs that can be safely deleted +func (ref ociReference) getBlobsToDelete(blobsUsedByDescriptorToDelete map[digest.Digest]int, sharedBlobsDir string) (*set.Set[digest.Digest], error) { + rootIndex, err := ref.getIndex() + if err != nil { + return nil, err + } + blobsUsedInRootIndex := make(map[digest.Digest]int) + err = ref.addBlobsUsedInIndex(blobsUsedInRootIndex, rootIndex, sharedBlobsDir) + if err != nil { + return nil, err + } + + blobsToDelete := set.New[digest.Digest]() + + for digest, count := range blobsUsedInRootIndex { + if count-blobsUsedByDescriptorToDelete[digest] == 0 { + blobsToDelete.Add(digest) + } + } + + return blobsToDelete, nil +} + +// This transport never generates layouts where blobs for an image are both in the local blobs directory +// and the shared one; it’s either one or the other, depending on how OCISharedBlobDirPath is set. +// +// But we can’t correctly compute use counts for OCISharedBlobDirPath (because we don't know what +// the other layouts sharing that directory are, and we might not even have permission to read them), +// so we can’t really delete any blobs in that case. +// Checking the _local_ blobs directory, and deleting blobs from there, doesn't really hurt, +// in case the layout was created using some other tool or without OCISharedBlobDirPath set, so let's silently +// check for local blobs (but we should make no noise if the blobs are actually in the shared directory). +// +// So, NOTE: the blobPath() call below hard-codes "" even in calls where OCISharedBlobDirPath is set +func (ref ociReference) deleteBlobs(blobsToDelete *set.Set[digest.Digest]) error { + for _, digest := range blobsToDelete.Values() { + blobPath, err := ref.blobPath(digest, "") //Only delete in the local directory, see comment above + if err != nil { + return err + } + err = deleteBlob(blobPath) + if err != nil { + return err + } + } + + return nil +} + +func deleteBlob(blobPath string) error { + logrus.Debug(fmt.Sprintf("Deleting blob at %q", blobPath)) + + err := os.Remove(blobPath) + if err != nil && !os.IsNotExist(err) { + return err + } else { + return nil + } +} + +func (ref ociReference) deleteReferenceFromIndex(referenceIndex int) error { + index, err := ref.getIndex() + if err != nil { + return err + } + + index.Manifests = slices.Delete(index.Manifests, referenceIndex, referenceIndex+1) + + return saveJSON(ref.indexPath(), index) +} + +func saveJSON(path string, content any) error { + // If the file already exists, get its mode to preserve it + var mode fs.FileMode + existingfi, err := os.Stat(path) + if err != nil { + if !os.IsNotExist(err) { + return err + } else { // File does not exist, use default mode + mode = 0644 + } + } else { + mode = existingfi.Mode() + } + + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) + if err != nil { + return err + } + defer file.Close() + + return json.NewEncoder(file).Encode(content) +} + +func (ref ociReference) getManifest(descriptor *imgspecv1.Descriptor, sharedBlobsDir string) (*imgspecv1.Manifest, error) { + manifestPath, err := ref.blobPath(descriptor.Digest, sharedBlobsDir) + if err != nil { + return nil, err + } + + manifest, err := parseJSON[imgspecv1.Manifest](manifestPath) + if err != nil { + return nil, err + } + + return manifest, nil +} diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go index 8ff43d448..100d16763 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go @@ -19,6 +19,7 @@ import ( digest "github.com/opencontainers/go-digest" imgspec "github.com/opencontainers/image-spec/specs-go" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/exp/slices" ) type ociImageDestination struct { @@ -84,7 +85,7 @@ func newImageDestination(sys *types.SystemContext, ref ociReference) (private.Im // Per the OCI image specification, layouts MUST have a "blobs" subdirectory, // but it MAY be empty (e.g. if we never end up calling PutBlob) // https://github.com/opencontainers/image-spec/blame/7c889fafd04a893f5c5f50b7ab9963d5d64e5242/image-layout.md#L19 - if err := ensureDirectoryExists(filepath.Join(d.ref.dir, "blobs")); err != nil { + if err := ensureDirectoryExists(filepath.Join(d.ref.dir, imgspecv1.ImageBlobsDir)); err != nil { return nil, err } return d, nil @@ -271,8 +272,8 @@ func (d *ociImageDestination) addManifest(desc *imgspecv1.Descriptor) { return } } - // It's a new entry to be added to the index. - d.index.Manifests = append(d.index.Manifests, *desc) + // It's a new entry to be added to the index. Use slices.Clone() to avoid a remote dependency on how d.index was created. + d.index.Manifests = append(slices.Clone(d.index.Manifests), *desc) } // Commit marks the process of storing the image as successful and asks for the image to be persisted. @@ -283,7 +284,13 @@ func (d *ociImageDestination) addManifest(desc *imgspecv1.Descriptor) { // - Uploaded data MAY be visible to others before Commit() is called // - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed) func (d *ociImageDestination) Commit(context.Context, types.UnparsedImage) error { - if err := os.WriteFile(d.ref.ociLayoutPath(), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil { + layoutBytes, err := json.Marshal(imgspecv1.ImageLayout{ + Version: imgspecv1.ImageLayoutVersion, + }) + if err != nil { + return err + } + if err := os.WriteFile(d.ref.ociLayoutPath(), layoutBytes, 0644); err != nil { return err } indexJSON, err := json.Marshal(d.index) diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go index 6b423f3b0..f5f1debc9 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go @@ -60,7 +60,7 @@ func newImageSource(sys *types.SystemContext, ref ociReference) (private.ImageSo client := &http.Client{} client.Transport = tr - descriptor, err := ref.getManifestDescriptor() + descriptor, _, err := ref.getManifestDescriptor() if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go b/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go index 6586b8440..1e26dc524 100644 --- a/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go +++ b/vendor/github.com/containers/image/v5/oci/layout/oci_transport.go @@ -160,48 +160,56 @@ func (ref ociReference) NewImage(ctx context.Context, sys *types.SystemContext) // getIndex returns a pointer to the index references by this ociReference. If an error occurs opening an index nil is returned together // with an error. func (ref ociReference) getIndex() (*imgspecv1.Index, error) { - indexJSON, err := os.Open(ref.indexPath()) + return parseIndex(ref.indexPath()) +} + +func parseIndex(path string) (*imgspecv1.Index, error) { + return parseJSON[imgspecv1.Index](path) +} + +func parseJSON[T any](path string) (*T, error) { + content, err := os.Open(path) if err != nil { return nil, err } - defer indexJSON.Close() + defer content.Close() - index := &imgspecv1.Index{} - if err := json.NewDecoder(indexJSON).Decode(index); err != nil { + obj := new(T) + if err := json.NewDecoder(content).Decode(obj); err != nil { return nil, err } - return index, nil + return obj, nil } -func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, error) { +func (ref ociReference) getManifestDescriptor() (imgspecv1.Descriptor, int, error) { index, err := ref.getIndex() if err != nil { - return imgspecv1.Descriptor{}, err + return imgspecv1.Descriptor{}, -1, err } if ref.image == "" { // return manifest if only one image is in the oci directory if len(index.Manifests) != 1 { // ask user to choose image when more than one image in the oci directory - return imgspecv1.Descriptor{}, ErrMoreThanOneImage + return imgspecv1.Descriptor{}, -1, ErrMoreThanOneImage } - return index.Manifests[0], nil + return index.Manifests[0], 0, nil } else { // if image specified, look through all manifests for a match var unsupportedMIMETypes []string - for _, md := range index.Manifests { + for i, md := range index.Manifests { if refName, ok := md.Annotations[imgspecv1.AnnotationRefName]; ok && refName == ref.image { if md.MediaType == imgspecv1.MediaTypeImageManifest || md.MediaType == imgspecv1.MediaTypeImageIndex { - return md, nil + return md, i, nil } unsupportedMIMETypes = append(unsupportedMIMETypes, md.MediaType) } } if len(unsupportedMIMETypes) != 0 { - return imgspecv1.Descriptor{}, fmt.Errorf("reference %q matches unsupported manifest MIME types %q", ref.image, unsupportedMIMETypes) + return imgspecv1.Descriptor{}, -1, fmt.Errorf("reference %q matches unsupported manifest MIME types %q", ref.image, unsupportedMIMETypes) } } - return imgspecv1.Descriptor{}, ImageNotFoundError{ref} + return imgspecv1.Descriptor{}, -1, ImageNotFoundError{ref} } // LoadManifestDescriptor loads the manifest descriptor to be used to retrieve the image name @@ -211,7 +219,8 @@ func LoadManifestDescriptor(imgRef types.ImageReference) (imgspecv1.Descriptor, if !ok { return imgspecv1.Descriptor{}, errors.New("error typecasting, need type ociRef") } - return ociRef.getManifestDescriptor() + md, _, err := ociRef.getManifestDescriptor() + return md, err } // NewImageSource returns a types.ImageSource for this reference. @@ -226,19 +235,14 @@ func (ref ociReference) NewImageDestination(ctx context.Context, sys *types.Syst return newImageDestination(sys, ref) } -// DeleteImage deletes the named image from the registry, if supported. -func (ref ociReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error { - return errors.New("Deleting images not implemented for oci: images") -} - // ociLayoutPath returns a path for the oci-layout within a directory using OCI conventions. func (ref ociReference) ociLayoutPath() string { - return filepath.Join(ref.dir, "oci-layout") + return filepath.Join(ref.dir, imgspecv1.ImageLayoutFile) } // indexPath returns a path for the index.json within a directory using OCI conventions. func (ref ociReference) indexPath() string { - return filepath.Join(ref.dir, "index.json") + return filepath.Join(ref.dir, imgspecv1.ImageIndexFile) } // blobPath returns a path for a blob within a directory using OCI image-layout conventions. @@ -246,9 +250,11 @@ func (ref ociReference) blobPath(digest digest.Digest, sharedBlobDir string) (st if err := digest.Validate(); err != nil { return "", fmt.Errorf("unexpected digest reference %s: %w", digest, err) } - blobDir := filepath.Join(ref.dir, "blobs") + var blobDir string if sharedBlobDir != "" { blobDir = sharedBlobDir + } else { + blobDir = filepath.Join(ref.dir, imgspecv1.ImageBlobsDir) } return filepath.Join(blobDir, digest.Algorithm().String(), digest.Hex()), nil } diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go index b987c5806..c61065cb0 100644 --- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go +++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io/fs" "os" "os/exec" "path/filepath" @@ -61,78 +62,6 @@ func newAuthPathDefault(path string) authPath { return authPath{path: path, legacyFormat: false} } -// SetCredentials stores the username and password in a location -// appropriate for sys and the users’ configuration. -// A valid key is a repository, a namespace within a registry, or a registry hostname; -// using forms other than just a registry may fail depending on configuration. -// Returns a human-readable description of the location that was updated. -// NOTE: The return value is only intended to be read by humans; its form is not an API, -// it may change (or new forms can be added) any time. -func SetCredentials(sys *types.SystemContext, key, username, password string) (string, error) { - isNamespaced, err := validateKey(key) - if err != nil { - return "", err - } - - helpers, err := sysregistriesv2.CredentialHelpers(sys) - if err != nil { - return "", err - } - - // Make sure to collect all errors. - var multiErr error - for _, helper := range helpers { - var desc string - var err error - switch helper { - // Special-case the built-in helpers for auth files. - case sysregistriesv2.AuthenticationFileHelper: - desc, err = modifyJSON(sys, func(fileContents *dockerConfigFile) (bool, string, error) { - if ch, exists := fileContents.CredHelpers[key]; exists { - if isNamespaced { - return false, "", unsupportedNamespaceErr(ch) - } - desc, err := setCredsInCredHelper(ch, key, username, password) - if err != nil { - return false, "", err - } - return false, desc, nil - } - creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) - newCreds := dockerAuthConfig{Auth: creds} - fileContents.AuthConfigs[key] = newCreds - return true, "", nil - }) - // External helpers. - default: - if isNamespaced { - err = unsupportedNamespaceErr(helper) - } else { - desc, err = setCredsInCredHelper(helper, key, username, password) - } - } - if err != nil { - multiErr = multierror.Append(multiErr, err) - logrus.Debugf("Error storing credentials for %s in credential helper %s: %v", key, helper, err) - continue - } - logrus.Debugf("Stored credentials for %s in credential helper %s", key, helper) - return desc, nil - } - return "", multiErr -} - -func unsupportedNamespaceErr(helper string) error { - return fmt.Errorf("namespaced key is not supported for credential helper %s", helper) -} - -// SetAuthentication stores the username and password in the credential helper or file -// See the documentation of SetCredentials for format of "key" -func SetAuthentication(sys *types.SystemContext, key, username, password string) error { - _, err := SetCredentials(sys, key, username, password) - return err -} - // GetAllCredentials returns the registry credentials for all registries stored // in any of the configured credential helpers. func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthConfig, error) { @@ -370,17 +299,79 @@ func getAuthenticationWithHomeDir(sys *types.SystemContext, key, homeDir string) return creds.Username, creds.Password, nil } -// RemoveAuthentication removes credentials for `key` from all possible -// sources such as credential helpers and auth files. +// SetCredentials stores the username and password in a location +// appropriate for sys and the users’ configuration. // A valid key is a repository, a namespace within a registry, or a registry hostname; // using forms other than just a registry may fail depending on configuration. -func RemoveAuthentication(sys *types.SystemContext, key string) error { - isNamespaced, err := validateKey(key) +// Returns a human-readable description of the location that was updated. +// NOTE: The return value is only intended to be read by humans; its form is not an API, +// it may change (or new forms can be added) any time. +func SetCredentials(sys *types.SystemContext, key, username, password string) (string, error) { + helpers, jsonEditor, key, isNamespaced, err := prepareForEdit(sys, key, true) if err != nil { - return err + return "", err } - helpers, err := sysregistriesv2.CredentialHelpers(sys) + // Make sure to collect all errors. + var multiErr error + for _, helper := range helpers { + var desc string + var err error + switch helper { + // Special-case the built-in helpers for auth files. + case sysregistriesv2.AuthenticationFileHelper: + desc, err = jsonEditor(sys, func(fileContents *dockerConfigFile) (bool, string, error) { + if ch, exists := fileContents.CredHelpers[key]; exists { + if isNamespaced { + return false, "", unsupportedNamespaceErr(ch) + } + desc, err := setCredsInCredHelper(ch, key, username, password) + if err != nil { + return false, "", err + } + return false, desc, nil + } + creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + newCreds := dockerAuthConfig{Auth: creds} + fileContents.AuthConfigs[key] = newCreds + return true, "", nil + }) + // External helpers. + default: + if isNamespaced { + err = unsupportedNamespaceErr(helper) + } else { + desc, err = setCredsInCredHelper(helper, key, username, password) + } + } + if err != nil { + multiErr = multierror.Append(multiErr, err) + logrus.Debugf("Error storing credentials for %s in credential helper %s: %v", key, helper, err) + continue + } + logrus.Debugf("Stored credentials for %s in credential helper %s", key, helper) + return desc, nil + } + return "", multiErr +} + +func unsupportedNamespaceErr(helper string) error { + return fmt.Errorf("namespaced key is not supported for credential helper %s", helper) +} + +// SetAuthentication stores the username and password in the credential helper or file +// See the documentation of SetCredentials for format of "key" +func SetAuthentication(sys *types.SystemContext, key, username, password string) error { + _, err := SetCredentials(sys, key, username, password) + return err +} + +// RemoveAuthentication removes credentials for `key` from all possible +// sources such as credential helpers and auth files. +// A valid key is a repository, a namespace within a registry, or a registry hostname; +// using forms other than just a registry may fail depending on configuration. +func RemoveAuthentication(sys *types.SystemContext, key string) error { + helpers, jsonEditor, key, isNamespaced, err := prepareForEdit(sys, key, true) if err != nil { return err } @@ -411,7 +402,7 @@ func RemoveAuthentication(sys *types.SystemContext, key string) error { switch helper { // Special-case the built-in helper for auth files. case sysregistriesv2.AuthenticationFileHelper: - _, err = modifyJSON(sys, func(fileContents *dockerConfigFile) (bool, string, error) { + _, err = jsonEditor(sys, func(fileContents *dockerConfigFile) (bool, string, error) { if innerHelper, exists := fileContents.CredHelpers[key]; exists { removeFromCredHelper(innerHelper) } @@ -443,7 +434,7 @@ func RemoveAuthentication(sys *types.SystemContext, key string) error { // RemoveAllAuthentication deletes all the credentials stored in credential // helpers and auth files. func RemoveAllAuthentication(sys *types.SystemContext) error { - helpers, err := sysregistriesv2.CredentialHelpers(sys) + helpers, jsonEditor, _, _, err := prepareForEdit(sys, "", false) if err != nil { return err } @@ -454,7 +445,7 @@ func RemoveAllAuthentication(sys *types.SystemContext) error { switch helper { // Special-case the built-in helper for auth files. case sysregistriesv2.AuthenticationFileHelper: - _, err = modifyJSON(sys, func(fileContents *dockerConfigFile) (bool, string, error) { + _, err = jsonEditor(sys, func(fileContents *dockerConfigFile) (bool, string, error) { for registry, helper := range fileContents.CredHelpers { // Helpers in auth files are expected // to exist, so no special treatment @@ -497,6 +488,46 @@ func RemoveAllAuthentication(sys *types.SystemContext) error { return multiErr } +// prepareForEdit processes sys and key (if keyRelevant) to return: +// - a list of credential helpers +// - a function which can be used to edit the JSON file +// - the key value to actually use in credential helpers / JSON +// - a boolean which is true if key is namespaced (and should not be used with credential helpers). +func prepareForEdit(sys *types.SystemContext, key string, keyRelevant bool) ([]string, func(*types.SystemContext, func(*dockerConfigFile) (bool, string, error)) (string, error), string, bool, error) { + var isNamespaced bool + if keyRelevant { + ns, err := validateKey(key) + if err != nil { + return nil, nil, "", false, err + } + isNamespaced = ns + } + + if sys != nil && sys.DockerCompatAuthFilePath != "" { + if sys.AuthFilePath != "" { + return nil, nil, "", false, errors.New("AuthFilePath and DockerCompatAuthFilePath can not be set simultaneously") + } + if keyRelevant { + if isNamespaced { + return nil, nil, "", false, fmt.Errorf("Credentials cannot be recorded in Docker-compatible format with namespaced key %q", key) + } + if key == "docker.io" { + key = "https://index.docker.io/v1/" + } + } + + // Do not use helpers defined in sysregistriesv2 because Docker isn’t aware of them. + return []string{sysregistriesv2.AuthenticationFileHelper}, modifyDockerConfigJSON, key, false, nil + } + + helpers, err := sysregistriesv2.CredentialHelpers(sys) + if err != nil { + return nil, nil, "", false, err + } + + return helpers, modifyJSON, key, isNamespaced, nil +} + func listCredsInCredHelper(credHelper string) (map[string]string, error) { helperName := fmt.Sprintf("docker-credential-%s", credHelper) p := helperclient.NewShellProgramFunc(helperName) @@ -513,9 +544,17 @@ func getPathToAuth(sys *types.SystemContext) (authPath, bool, error) { // it exists only to allow testing it with an artificial runtime.GOOS. func getPathToAuthWithOS(sys *types.SystemContext, goOS string) (authPath, bool, error) { if sys != nil { + if sys.AuthFilePath != "" && sys.DockerCompatAuthFilePath != "" { + return authPath{}, false, errors.New("AuthFilePath and DockerCompatAuthFilePath can not be set simultaneously") + } if sys.AuthFilePath != "" { return newAuthPathDefault(sys.AuthFilePath), true, nil } + // When reading, we can process auth.json and Docker’s config.json with the same code. + // When writing, prepareForEdit chooses an appropriate jsonEditor implementation. + if sys.DockerCompatAuthFilePath != "" { + return newAuthPathDefault(sys.DockerCompatAuthFilePath), true, nil + } if sys.LegacyFormatAuthFilePath != "" { return authPath{path: sys.LegacyFormatAuthFilePath, legacyFormat: true}, true, nil } @@ -626,6 +665,86 @@ func modifyJSON(sys *types.SystemContext, editor func(fileContents *dockerConfig return description, nil } +// modifyDockerConfigJSON finds a docker config.json file, calls editor on the contents, and +// writes it back if editor returns true. +// Returns a human-readable description of the file, to be returned by SetCredentials. +// +// The editor may also return a human-readable description of the updated location; if it is "", +// the file itself is used. +func modifyDockerConfigJSON(sys *types.SystemContext, editor func(fileContents *dockerConfigFile) (bool, string, error)) (string, error) { + if sys == nil || sys.DockerCompatAuthFilePath == "" { + return "", errors.New("internal error: modifyDockerConfigJSON called with DockerCompatAuthFilePath not set") + } + path := sys.DockerCompatAuthFilePath + + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0700); err != nil { + return "", err + } + + // Try hard not to clobber fields we don’t understand, even fields which may be added in future Docker versions. + var rawContents map[string]json.RawMessage + originalBytes, err := os.ReadFile(path) + switch { + case err == nil: + if err := json.Unmarshal(originalBytes, &rawContents); err != nil { + return "", fmt.Errorf("unmarshaling JSON at %q: %w", path, err) + } + case errors.Is(err, fs.ErrNotExist): + rawContents = map[string]json.RawMessage{} + default: // err != nil + return "", err + } + + syntheticContents := dockerConfigFile{ + AuthConfigs: map[string]dockerAuthConfig{}, + CredHelpers: map[string]string{}, + } + // json.Unmarshal also falls back to case-insensitive field matching; this code does not do that. Presumably + // config.json is mostly maintained by machines doing `docker login`, so the files should, hopefully, not contain field names with + // unexpected case. + if rawAuths, ok := rawContents["auths"]; ok { + // This conversion will lose fields we don’t know about; when updating an entry, we can’t tell whether an unknown field + // should be preserved or discarded (because it is made obsolete/unwanted with the new credentials). + // It might make sense to track which entries of "auths" we actually modified, and to not touch any others. + if err := json.Unmarshal(rawAuths, &syntheticContents.AuthConfigs); err != nil { + return "", fmt.Errorf(`unmarshaling "auths" in JSON at %q: %w`, path, err) + } + } + if rawCH, ok := rawContents["credHelpers"]; ok { + if err := json.Unmarshal(rawCH, &syntheticContents.CredHelpers); err != nil { + return "", fmt.Errorf(`unmarshaling "credHelpers" in JSON at %q: %w`, path, err) + + } + } + + updated, description, err := editor(&syntheticContents) + if err != nil { + return "", fmt.Errorf("updating %q: %w", path, err) + } + if updated { + rawAuths, err := json.MarshalIndent(syntheticContents.AuthConfigs, "", "\t") + if err != nil { + return "", fmt.Errorf("marshaling JSON %q: %w", path, err) + } + rawContents["auths"] = rawAuths + // We never modify syntheticContents.CredHelpers, so we don’t need to update it. + newData, err := json.MarshalIndent(rawContents, "", "\t") + if err != nil { + return "", fmt.Errorf("marshaling JSON %q: %w", path, err) + } + + if err = ioutils.AtomicWriteFile(path, newData, 0600); err != nil { + return "", fmt.Errorf("writing to file %q: %w", path, err) + } + } + + if description == "" { + description = path + } + return description, nil +} + func getCredsFromCredHelper(credHelper, registry string) (types.DockerAuthConfig, error) { helperName := fmt.Sprintf("docker-credential-%s", credHelper) p := helperclient.NewShellProgramFunc(helperName) diff --git a/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go b/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go index 56b0d4939..c6ec84bd5 100644 --- a/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go +++ b/vendor/github.com/containers/image/v5/pkg/tlsclientconfig/tlsclientconfig.go @@ -66,7 +66,7 @@ func SetupCertificates(dir string, tlsc *tls.Config) error { if err != nil { return err } - tlsc.Certificates = append(tlsc.Certificates, cert) + tlsc.Certificates = append(slices.Clone(tlsc.Certificates), cert) } if strings.HasSuffix(f.Name(), ".key") { keyName := f.Name() diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go index 33adb5f1d..180a98c5b 100644 --- a/vendor/github.com/containers/image/v5/types/types.go +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -445,7 +445,7 @@ type ImageCloser interface { Close() error } -// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest +// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedImage type ManifestUpdateOptions struct { LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls+annotations) which should replace the originals, in order (the root layer first, and then successive layered layers). BlobInfos' MediaType fields are ignored. EmbeddedDockerReference reference.Named @@ -457,7 +457,7 @@ type ManifestUpdateOptions struct { // ManifestUpdateInformation is a component of ManifestUpdateOptions, named here // only to make writing struct literals possible. type ManifestUpdateInformation struct { - Destination ImageDestination // and yes, UpdatedManifest may write to Destination (see the schema2 → schema1 conversion logic in image/docker_schema2.go) + Destination ImageDestination // and yes, UpdatedImage may write to Destination (see the schema2 → schema1 conversion logic in image/docker_schema2.go) LayerInfos []BlobInfo // Complete BlobInfos (size+digest) which have been uploaded, in order (the root layer first, and then successive layered layers) LayerDiffIDs []digest.Digest // Digest values for the _uncompressed_ contents of the blobs which have been uploaded, in the same order. } @@ -594,6 +594,10 @@ type SystemContext struct { // this field is ignored if `AuthFilePath` is set (we favor the newer format); // only reading of this data is supported; LegacyFormatAuthFilePath string + // If set, a path to a Docker-compatible "config.json" file containing credentials; and no other files are processed. + // This must not be set if AuthFilePath is set. + // Only credentials and credential helpers in this file apre processed, not any other configuration in this file. + DockerCompatAuthFilePath string // If not "", overrides the use of platform.GOARCH when choosing an image or verifying architecture match. ArchitectureChoice string // If not "", overrides the use of platform.GOOS when choosing an image or verifying OS match. diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 27d034dc5..990f0a96d 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -6,7 +6,7 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 5 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 28 + VersionMinor = 29 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 diff --git a/vendor/github.com/containers/storage/pkg/system/rm.go b/vendor/github.com/containers/storage/pkg/system/rm.go index 5917fa251..12243707a 100644 --- a/vendor/github.com/containers/storage/pkg/system/rm.go +++ b/vendor/github.com/containers/storage/pkg/system/rm.go @@ -28,7 +28,7 @@ func EnsureRemoveAll(dir string) error { // track retries exitOnErr := make(map[string]int) - maxRetry := 100 + maxRetry := 1000 // Attempt a simple remove all first, this avoids the more expensive // RecursiveUnmount call if not needed. @@ -38,7 +38,7 @@ func EnsureRemoveAll(dir string) error { // Attempt to unmount anything beneath this dir first if err := mount.RecursiveUnmount(dir); err != nil { - logrus.Debugf("RecusiveUnmount on %s failed: %v", dir, err) + logrus.Debugf("RecursiveUnmount on %s failed: %v", dir, err) } for { @@ -94,6 +94,6 @@ func EnsureRemoveAll(dir string) error { return err } exitOnErr[pe.Path]++ - time.Sleep(100 * time.Millisecond) + time.Sleep(10 * time.Millisecond) } } diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go index 86ac12eca..480e2fcb0 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_darwin.go @@ -25,6 +25,11 @@ func GetRootlessUID() int { return os.Getuid() } +// GetRootlessGID returns the GID of the user in the parent userNS +func GetRootlessGID() int { + return os.Getgid() +} + // RootlessEnv returns the environment settings for the rootless containers func RootlessEnv() []string { return append(os.Environ(), UsernsEnvName+"=") diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go index e169633d0..a8dc1ba03 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_linux.go @@ -441,6 +441,16 @@ func GetRootlessUID() int { return os.Getuid() } +// GetRootlessGID returns the GID of the user in the parent userNS +func GetRootlessGID() int { + gidEnv := getenv("_CONTAINERS_ROOTLESS_GID") + if gidEnv != "" { + u, _ := strconv.Atoi(gidEnv) + return u + } + return os.Getgid() +} + // RootlessEnv returns the environment settings for the rootless containers func RootlessEnv() []string { return append(os.Environ(), UsernsEnvName+"=done") diff --git a/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go index 66dd54596..83de680c2 100644 --- a/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go +++ b/vendor/github.com/containers/storage/pkg/unshare/unshare_unsupported.go @@ -25,6 +25,11 @@ func GetRootlessUID() int { return os.Getuid() } +// GetRootlessGID returns the GID of the user in the parent userNS +func GetRootlessGID() int { + return os.Getgid() +} + // RootlessEnv returns the environment settings for the rootless containers func RootlessEnv() []string { return append(os.Environ(), UsernsEnvName+"=") diff --git a/vendor/modules.txt b/vendor/modules.txt index 7c56058bc..cde12ebd0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -36,7 +36,7 @@ github.com/cloudfoundry/dropsonde/runtime_stats # github.com/cloudfoundry/sonde-go v0.0.0-20230911203642-fa89d986ae20 ## explicit; go 1.17 github.com/cloudfoundry/sonde-go/events -# github.com/containers/image/v5 v5.28.0 +# github.com/containers/image/v5 v5.29.0 ## explicit; go 1.19 github.com/containers/image/v5/directory/explicitfilepath github.com/containers/image/v5/docker @@ -60,6 +60,7 @@ github.com/containers/image/v5/internal/set github.com/containers/image/v5/internal/signature github.com/containers/image/v5/internal/streamdigest github.com/containers/image/v5/internal/tmpdir +github.com/containers/image/v5/internal/unparsedimage github.com/containers/image/v5/internal/uploadreader github.com/containers/image/v5/internal/useragent github.com/containers/image/v5/manifest @@ -82,7 +83,7 @@ github.com/containers/libtrust # github.com/containers/ocicrypt v1.1.9 ## explicit; go 1.20 github.com/containers/ocicrypt/spec -# github.com/containers/storage v1.50.2 +# github.com/containers/storage v1.51.0 ## explicit; go 1.19 github.com/containers/storage/pkg/chunked/compressor github.com/containers/storage/pkg/chunked/internal