From 4ee2946bbc0caf3db3ed6ef0cc7aa2db100b6477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Mon, 14 Aug 2023 20:24:51 +0200 Subject: [PATCH] Update c/image after https://github.com/containers/image/pull/2070 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > go get github.com/containers/image/v5@main > make vendor This moves c/image to a commit that includes both the work on main that we were already vendoring, and the last tagged version 5.27.0. That should prevent Renovate from proposing downgrades which fail tests: - https://github.com/containers/skopeo/pull/2065 - https://github.com/containers/skopeo/pull/2066 Signed-off-by: Miloslav Trmač --- go.mod | 16 +- go.sum | 34 +- .../containers/image/v5/copy/copy.go | 24 +- .../containers/image/v5/copy/multiple.go | 15 +- .../containers/image/v5/copy/single.go | 24 +- .../image/v5/docker/docker_client.go | 86 ++++- .../image/v5/docker/docker_image_dest.go | 5 + .../containers/image/v5/docker/errors.go | 7 +- .../containers/image/v5/internal/image/oci.go | 6 +- .../internal/manifest/docker_schema2_list.go | 66 ++-- .../image/v5/internal/manifest/errors.go | 22 +- .../image/v5/internal/manifest/oci_index.go | 59 +-- .../internal/pkg/platform/platform_matcher.go | 4 + .../containers/image/v5/manifest/oci.go | 4 +- .../image/v5/storage/storage_dest.go | 171 +++++---- .../go-containerregistry/pkg/v1/config.go | 151 ++++++++ .../google/go-containerregistry/pkg/v1/doc.go | 18 + .../go-containerregistry/pkg/v1/hash.go | 123 +++++++ .../go-containerregistry/pkg/v1/image.go | 59 +++ .../go-containerregistry/pkg/v1/index.go | 43 +++ .../go-containerregistry/pkg/v1/layer.go | 42 +++ .../go-containerregistry/pkg/v1/manifest.go | 71 ++++ .../go-containerregistry/pkg/v1/platform.go | 149 ++++++++ .../go-containerregistry/pkg/v1/progress.go | 25 ++ .../pkg/v1/types/types.go | 98 +++++ .../pkg/v1/zz_deepcopy_generated.go | 339 ++++++++++++++++++ .../sylabs/sif/v2/pkg/sif/descriptor.go | 54 +++ .../sylabs/sif/v2/pkg/sif/descriptor_input.go | 9 + .../sylabs/sif/v2/pkg/sif/select.go | 14 +- .../github.com/sylabs/sif/v2/pkg/sif/sif.go | 6 + vendor/golang.org/x/net/http2/transport.go | 35 +- .../x/oauth2/internal/client_appengine.go | 1 - vendor/modules.txt | 22 +- 33 files changed, 1596 insertions(+), 206 deletions(-) create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/config.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/doc.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/hash.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/image.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/index.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/layer.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/platform.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/progress.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go create mode 100644 vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go diff --git a/go.mod b/go.mod index 9d68bfa8..10682b62 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/containers/common v0.55.0 - github.com/containers/image/v5 v5.26.1-0.20230802064408-aca060028898 + github.com/containers/image/v5 v5.27.1-0.20230814071742-35192da58823 github.com/containers/ocicrypt v1.1.7 github.com/containers/storage v1.48.1-0.20230728131509-c3da76fa3f63 github.com/docker/distribution v2.8.2+incompatible @@ -58,7 +58,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-containerregistry v0.15.2 // indirect + github.com/google/go-containerregistry v0.16.1 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -95,10 +95,10 @@ require ( github.com/segmentio/ksuid v1.0.4 // indirect github.com/sigstore/fulcio v1.4.0 // indirect github.com/sigstore/rekor v1.2.2 // indirect - github.com/sigstore/sigstore v1.7.1 // indirect + github.com/sigstore/sigstore v1.7.2 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect - github.com/sylabs/sif/v2 v2.11.5 // indirect + github.com/sylabs/sif/v2 v2.12.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/theupdateframework/go-tuf v0.5.2 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect @@ -115,13 +115,13 @@ require ( go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect - golang.org/x/crypto v0.11.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/oauth2 v0.11.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.9.3 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect diff --git a/go.sum b/go.sum index 3bd22fe1..e1b6ca0d 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSk github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/containers/common v0.55.0 h1:0z/INPFrc9NPcx2iqU3OA333BTNequU3GPCvuDKyHkg= github.com/containers/common v0.55.0/go.mod h1:JNNJY++mDJW7xTNeU08b6h11omV0FYl9diWLofkgaY0= -github.com/containers/image/v5 v5.26.1-0.20230802064408-aca060028898 h1:GtIYGr9dIgdOSFCociw+PetXxrJZF0CWwP4OlCPt1bE= -github.com/containers/image/v5 v5.26.1-0.20230802064408-aca060028898/go.mod h1:i5/5QXqhMfpDcpxIMpP/2pj3jIonvrhn1J0r8air7sI= +github.com/containers/image/v5 v5.27.1-0.20230814071742-35192da58823 h1:6NtRG/T1HcerlVS5lK/VjA8A8oSKdWZEcbXnd6PIE6k= +github.com/containers/image/v5 v5.27.1-0.20230814071742-35192da58823/go.mod h1:DMgY2TJ+xxLkzBFqxS5I6TtG36wYL4R/Yy+G76gDhZk= 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.7 h1:thhNr4fu2ltyGz8aMx8u48Ae0Pnbip3ePP9/mzkZ/3U= @@ -116,7 +116,7 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-rod/rod v0.113.3 h1:oLiKZW721CCMwA5g7977cWfcAKQ+FuosP47Zf1QiDrA= +github.com/go-rod/rod v0.114.2 h1:Qwt+vZHHnb117zc0q+XjhAJCkB01hchWSxH/raCyLb4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= @@ -174,8 +174,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= -github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= +github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= +github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -316,8 +316,8 @@ github.com/sigstore/fulcio v1.4.0 h1:05+k8BFvwTQzfCkVxESWzCN4b70KIRliGYz0Upmdrs8 github.com/sigstore/fulcio v1.4.0/go.mod h1:wcjlktbhoy6+ZTxO3yXpvqUxsLV+JEH4FF3a5Jz4VPI= github.com/sigstore/rekor v1.2.2 h1:5JK/zKZvcQpL/jBmHvmFj3YbpDMBQnJQ6ygp8xdF3bY= github.com/sigstore/rekor v1.2.2/go.mod h1:FGnWBGWzeNceJnp0x9eDFd41mI8aQqCjj+Zp0IEs0Qg= -github.com/sigstore/sigstore v1.7.1 h1:fCATemikcBK0cG4+NcM940MfoIgmioY1vC6E66hXxks= -github.com/sigstore/sigstore v1.7.1/go.mod h1:0PmMzfJP2Y9+lugD0wer4e7TihR5tM7NcIs3bQNk5xg= +github.com/sigstore/sigstore v1.7.2 h1:MY0wSOhKWa8SIWSCO9SzFnUl+b7jbthgXHJpuUg31Qs= +github.com/sigstore/sigstore v1.7.2/go.mod h1:2IPD5YXrXoznfnIoVsDF7ARC1Nha8xIdLpsC4kEQh5w= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -347,8 +347,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM= -github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y= +github.com/sylabs/sif/v2 v2.12.0 h1:mghM4elFasymxIWYZ8q/eLnDRMOOhRrrKLrEceVQm08= +github.com/sylabs/sif/v2 v2.12.0/go.mod h1:Gn6bQvH4q4CH2h8BR9Qkuc1LvhYsfhOxCU7U6gS8jto= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= @@ -417,8 +417,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= @@ -442,11 +442,11 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -484,8 +484,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index ac0e6f2f..11b2dbfd 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -133,6 +133,10 @@ type Options struct { // Invalid when copying a non-multi-architecture image. That will probably // change in the future. EnsureCompressionVariantsExist []OptionCompressionVariant + // ForceCompressionFormat ensures that the compression algorithm set in + // DestinationCtx.CompressionFormat is used exclusively, and blobs of other + // compression algorithms are not reused. + ForceCompressionFormat bool } // OptionCompressionVariant allows to supply information about @@ -163,6 +167,14 @@ type copier struct { signersToClose []*signer.Signer // Signers that should be closed when this copier is destroyed. } +// Internal function to validate `requireCompressionFormatMatch` for copySingleImageOptions +func shouldRequireCompressionFormatMatch(options *Options) (bool, error) { + if options.ForceCompressionFormat && (options.DestinationCtx == nil || options.DestinationCtx.CompressionFormat == nil) { + return false, fmt.Errorf("cannot use ForceCompressionFormat with undefined default compression format") + } + return options.ForceCompressionFormat, nil +} + // Image copies image from srcRef to destRef, using policyContext to validate // source image admissibility. It returns the manifest which was written to // the new copy of the image. @@ -269,8 +281,12 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, if len(options.EnsureCompressionVariantsExist) > 0 { return nil, fmt.Errorf("EnsureCompressionVariantsExist is not implemented when not creating a multi-architecture image") } + requireCompressionFormatMatch, err := shouldRequireCompressionFormatMatch(options) + if err != nil { + return nil, err + } // The simple case: just copy a single image. - single, err := c.copySingleImage(ctx, c.unparsedToplevel, nil, copySingleImageOptions{requireCompressionFormatMatch: false}) + single, err := c.copySingleImage(ctx, c.unparsedToplevel, nil, copySingleImageOptions{requireCompressionFormatMatch: requireCompressionFormatMatch}) if err != nil { return nil, err } @@ -279,6 +295,10 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, if len(options.EnsureCompressionVariantsExist) > 0 { return nil, fmt.Errorf("EnsureCompressionVariantsExist is not implemented when not creating a multi-architecture image") } + requireCompressionFormatMatch, err := shouldRequireCompressionFormatMatch(options) + if err != nil { + return nil, err + } // This is a manifest list, and we weren't asked to copy multiple images. Choose a single image that // matches the current system to copy, and copy it. mfest, manifestType, err := c.unparsedToplevel.Manifest(ctx) @@ -295,7 +315,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, } logrus.Debugf("Source is a manifest list; copying (only) instance %s for current system", instanceDigest) unparsedInstance := image.UnparsedInstance(rawSource, &instanceDigest) - single, err := c.copySingleImage(ctx, unparsedInstance, nil, copySingleImageOptions{requireCompressionFormatMatch: false}) + single, err := c.copySingleImage(ctx, unparsedInstance, nil, copySingleImageOptions{requireCompressionFormatMatch: requireCompressionFormatMatch}) if err != nil { return nil, fmt.Errorf("copying system image from manifest list: %w", err) } diff --git a/vendor/github.com/containers/image/v5/copy/multiple.go b/vendor/github.com/containers/image/v5/copy/multiple.go index 34f2129d..30f6da25 100644 --- a/vendor/github.com/containers/image/v5/copy/multiple.go +++ b/vendor/github.com/containers/image/v5/copy/multiple.go @@ -32,6 +32,10 @@ type instanceCopy struct { op instanceCopyKind sourceDigest digest.Digest + // Fields which can be used by callers when operation + // is `instanceCopyCopy` + copyForceCompressionFormat bool + // Fields which can be used by callers when operation // is `instanceCopyClone` cloneCompressionVariant OptionCompressionVariant @@ -122,9 +126,14 @@ func prepareInstanceCopies(list internalManifest.List, instanceDigests []digest. if err != nil { return res, fmt.Errorf("getting details for instance %s: %w", instanceDigest, err) } + forceCompressionFormat, err := shouldRequireCompressionFormatMatch(options) + if err != nil { + return nil, err + } res = append(res, instanceCopy{ - op: instanceCopyCopy, - sourceDigest: instanceDigest, + op: instanceCopyCopy, + sourceDigest: instanceDigest, + copyForceCompressionFormat: forceCompressionFormat, }) platform := platformV1ToPlatformComparable(instanceDetails.ReadOnly.Platform) compressionList := compressionsByPlatform[platform] @@ -230,7 +239,7 @@ func (c *copier) copyMultipleImages(ctx context.Context) (copiedManifest []byte, logrus.Debugf("Copying instance %s (%d/%d)", instance.sourceDigest, i+1, len(instanceCopyList)) c.Printf("Copying image %s (%d/%d)\n", instance.sourceDigest, i+1, len(instanceCopyList)) unparsedInstance := image.UnparsedInstance(c.rawSource, &instanceCopyList[i].sourceDigest) - updated, err := c.copySingleImage(ctx, unparsedInstance, &instanceCopyList[i].sourceDigest, copySingleImageOptions{requireCompressionFormatMatch: false}) + updated, err := c.copySingleImage(ctx, unparsedInstance, &instanceCopyList[i].sourceDigest, copySingleImageOptions{requireCompressionFormatMatch: instance.copyForceCompressionFormat}) if err != nil { return nil, fmt.Errorf("copying image %d/%d from manifest list: %w", i+1, len(instanceCopyList), err) } diff --git a/vendor/github.com/containers/image/v5/copy/single.go b/vendor/github.com/containers/image/v5/copy/single.go index 5297f019..bb58ae65 100644 --- a/vendor/github.com/containers/image/v5/copy/single.go +++ b/vendor/github.com/containers/image/v5/copy/single.go @@ -305,18 +305,18 @@ func checkImageDestinationForCurrentRuntime(ctx context.Context, sys *types.Syst options := newOrderedSet() match := false for _, wantedPlatform := range wantedPlatforms { - // Waiting for https://github.com/opencontainers/image-spec/pull/777 : - // This currently can’t use image.MatchesPlatform because we don’t know what to use - // for image.Variant. - if wantedPlatform.OS == c.OS && wantedPlatform.Architecture == c.Architecture { + // For a transitional period, this might trigger warnings because the Variant + // field was added to OCI config only recently. If this turns out to be too noisy, + // revert this check to only look for (OS, Architecture). + if platform.MatchesPlatform(c.Platform, wantedPlatform) { match = true break } - options.append(fmt.Sprintf("%s+%s", wantedPlatform.OS, wantedPlatform.Architecture)) + options.append(fmt.Sprintf("%s+%s+%q", wantedPlatform.OS, wantedPlatform.Architecture, wantedPlatform.Variant)) } if !match { - logrus.Infof("Image operating system mismatch: image uses OS %q+architecture %q, expecting one of %q", - c.OS, c.Architecture, strings.Join(options.list, ", ")) + logrus.Infof("Image operating system mismatch: image uses OS %q+architecture %q+%q, expecting one of %q", + c.OS, c.Architecture, c.Variant, strings.Join(options.list, ", ")) } } return nil @@ -460,8 +460,14 @@ func (ic *imageCopier) copyLayers(ctx context.Context) ([]compressiontypes.Algor encryptAll = len(*ic.c.options.OciEncryptLayers) == 0 totalLayers := len(srcInfos) for _, l := range *ic.c.options.OciEncryptLayers { - // if layer is negative, it is reverse indexed. - layersToEncrypt.Add((totalLayers + l) % totalLayers) + switch { + case l >= 0 && l < totalLayers: + layersToEncrypt.Add(l) + case l < 0 && l+totalLayers >= 0: // Implies (l + totalLayers) < totalLayers + layersToEncrypt.Add(l + totalLayers) // If l is negative, it is reverse indexed. + default: + return nil, fmt.Errorf("when choosing layers to encrypt, layer index %d out of range (%d layers exist)", l, totalLayers) + } } if encryptAll { 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 dd9127c5..288dd1a9 100644 --- a/vendor/github.com/containers/image/v5/docker/docker_client.go +++ b/vendor/github.com/containers/image/v5/docker/docker_client.go @@ -1,7 +1,6 @@ package docker import ( - "bytes" "context" "crypto/tls" "encoding/json" @@ -19,6 +18,7 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/iolimits" + "github.com/containers/image/v5/internal/set" "github.com/containers/image/v5/internal/useragent" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/pkg/docker/config" @@ -121,6 +121,9 @@ type dockerClient struct { // Private state for detectProperties: detectPropertiesOnce sync.Once // detectPropertiesOnce is used to execute detectProperties() at most once. detectPropertiesError error // detectPropertiesError caches the initial error. + // Private state for logResponseWarnings + reportedWarningsLock sync.Mutex + reportedWarnings *set.Set[string] } type authScope struct { @@ -281,10 +284,11 @@ func newDockerClient(sys *types.SystemContext, registry, reference string) (*doc } return &dockerClient{ - sys: sys, - registry: registry, - userAgent: userAgent, - tlsClientConfig: tlsClientConfig, + sys: sys, + registry: registry, + userAgent: userAgent, + tlsClientConfig: tlsClientConfig, + reportedWarnings: set.New[string](), }, nil } @@ -624,9 +628,76 @@ func (c *dockerClient) makeRequestToResolvedURLOnce(ctx context.Context, method if err != nil { return nil, err } + if warnings := res.Header.Values("Warning"); len(warnings) != 0 { + c.logResponseWarnings(res, warnings) + } return res, nil } +// logResponseWarnings logs warningHeaders from res, if any. +func (c *dockerClient) logResponseWarnings(res *http.Response, warningHeaders []string) { + c.reportedWarningsLock.Lock() + defer c.reportedWarningsLock.Unlock() + + for _, header := range warningHeaders { + warningString := parseRegistryWarningHeader(header) + if warningString == "" { + logrus.Debugf("Ignored Warning: header from registry: %q", header) + } else { + if !c.reportedWarnings.Contains(warningString) { + c.reportedWarnings.Add(warningString) + // Note that reportedWarnings is based only on warningString, so that we don’t + // repeat the same warning for every request - but the warning includes the URL; + // so it may not be specific to that URL. + logrus.Warnf("Warning from registry (first encountered at %q): %q", res.Request.URL.Redacted(), warningString) + } else { + logrus.Debugf("Repeated warning from registry at %q: %q", res.Request.URL.Redacted(), warningString) + } + } + } +} + +// parseRegistryWarningHeader parses a Warning: header per RFC 7234, limited to the warning +// values allowed by opencontainers/distribution-spec. +// It returns the warning string if the header has the expected format, or "" otherwise. +func parseRegistryWarningHeader(header string) string { + const expectedPrefix = `299 - "` + const expectedSuffix = `"` + + // warning-value = warn-code SP warn-agent SP warn-text [ SP warn-date ] + // distribution-spec requires warn-code=299, warn-agent="-", warn-date missing + if !strings.HasPrefix(header, expectedPrefix) || !strings.HasSuffix(header, expectedSuffix) { + return "" + } + header = header[len(expectedPrefix) : len(header)-len(expectedSuffix)] + + // ”Recipients that process the value of a quoted-string MUST handle a quoted-pair + // as if it were replaced by the octet following the backslash.”, so let’s do that… + res := strings.Builder{} + afterBackslash := false + for _, c := range []byte(header) { // []byte because escaping is defined in terms of bytes, not Unicode code points + switch { + case c == 0x7F || (c < ' ' && c != '\t'): + return "" // Control characters are forbidden + case afterBackslash: + res.WriteByte(c) + afterBackslash = false + case c == '"': + // This terminates the warn-text and warn-date, forbidden by distribution-spec, follows, + // or completely invalid input. + return "" + case c == '\\': + afterBackslash = true + default: + res.WriteByte(c) + } + } + if afterBackslash { + return "" + } + return res.String() +} + // we're using the challenges from the /v2/ ping response and not the one from the destination // URL in this request because: // @@ -1008,9 +1079,10 @@ func isManifestUnknownError(err error) bool { if errors.As(err, &e) && e.ErrorCode() == errcode.ErrorCodeUnknown && e.Message == "Not Found" { return true } - // ALSO registry.redhat.io as of October 2022 + // opencontainers/distribution-spec does not require the errcode.Error payloads to be used, + // but specifies that the HTTP status must be 404. var unexpected *unexpectedHTTPResponseError - if errors.As(err, &unexpected) && unexpected.StatusCode == http.StatusNotFound && bytes.Contains(unexpected.Response, []byte("Not found")) { + if errors.As(err, &unexpected) && unexpected.StatusCode == http.StatusNotFound { return true } return false 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 63e372d6..0e7b154c 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 @@ -367,6 +367,11 @@ func (d *dockerImageDestination) TryReusingBlobWithOptions(ctx context.Context, // 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 } diff --git a/vendor/github.com/containers/image/v5/docker/errors.go b/vendor/github.com/containers/image/v5/docker/errors.go index 2caa10d7..e0396918 100644 --- a/vendor/github.com/containers/image/v5/docker/errors.go +++ b/vendor/github.com/containers/image/v5/docker/errors.go @@ -47,7 +47,12 @@ func httpResponseToError(res *http.Response, context string) error { } // registryHTTPResponseToError creates a Go error from an HTTP error response of a docker/distribution -// registry +// registry. +// +// WARNING: The OCI distribution spec says +// “A `4XX` response code from the registry MAY return a body in any format.”; but if it is +// JSON, it MUST use the errcode.Error structure. +// So, callers should primarily decide based on HTTP StatusCode, not based on error type here. func registryHTTPResponseToError(res *http.Response) error { err := handleErrorResponse(res) // len(errs) == 0 should never be returned by handleErrorResponse; if it does, we don't modify it and let the caller report it as is. 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 166daa0e..c6c0fb36 100644 --- a/vendor/github.com/containers/image/v5/internal/image/oci.go +++ b/vendor/github.com/containers/image/v5/internal/image/oci.go @@ -86,7 +86,7 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) { // old image manifests work (docker v2s1 especially). func (m *manifestOCI1) OCIConfig(ctx context.Context) (*imgspecv1.Image, error) { if m.m.Config.MediaType != imgspecv1.MediaTypeImageConfig { - return nil, internalManifest.NewNonImageArtifactError(m.m.Config.MediaType) + return nil, internalManifest.NewNonImageArtifactError(&m.m.Manifest) } cb, err := m.ConfigBlob(ctx) @@ -200,7 +200,7 @@ func (m *manifestOCI1) convertToManifestSchema2Generic(ctx context.Context, opti // This does not change the state of the original manifestOCI1 object. func (m *manifestOCI1) convertToManifestSchema2(_ context.Context, _ *types.ManifestUpdateOptions) (*manifestSchema2, error) { if m.m.Config.MediaType != imgspecv1.MediaTypeImageConfig { - return nil, internalManifest.NewNonImageArtifactError(m.m.Config.MediaType) + return nil, internalManifest.NewNonImageArtifactError(&m.m.Manifest) } // Create a copy of the descriptor. @@ -244,7 +244,7 @@ func (m *manifestOCI1) convertToManifestSchema2(_ context.Context, _ *types.Mani // This does not change the state of the original manifestOCI1 object. func (m *manifestOCI1) convertToManifestSchema1(ctx context.Context, options *types.ManifestUpdateOptions) (genericManifest, error) { if m.m.Config.MediaType != imgspecv1.MediaTypeImageConfig { - return nil, internalManifest.NewNonImageArtifactError(m.m.Config.MediaType) + return nil, internalManifest.NewNonImageArtifactError(&m.m.Manifest) } // We can't directly convert images to V1, but we can transitively convert via a V2 image 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 14a47664..357e2f3d 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 @@ -64,13 +64,8 @@ func (list *Schema2ListPublic) Instance(instanceDigest digest.Digest) (ListUpdat MediaType: manifest.MediaType, } ret.ReadOnly.CompressionAlgorithmNames = []string{compression.GzipAlgorithmName} - ret.ReadOnly.Platform = &imgspecv1.Platform{ - OS: manifest.Platform.OS, - Architecture: manifest.Platform.Architecture, - OSVersion: manifest.Platform.OSVersion, - OSFeatures: manifest.Platform.OSFeatures, - Variant: manifest.Platform.Variant, - } + platform := ociPlatformFromSchema2PlatformSpec(manifest.Platform) + ret.ReadOnly.Platform = &platform return ret, nil } } @@ -119,17 +114,20 @@ func (index *Schema2ListPublic) editInstances(editInstances []ListEdit) error { } index.Manifests[targetIndex].MediaType = editInstance.UpdateMediaType case ListOpAdd: - addInstance := Schema2ManifestDescriptor{ - Schema2Descriptor{Digest: editInstance.AddDigest, Size: editInstance.AddSize, MediaType: editInstance.AddMediaType}, - Schema2PlatformSpec{ - OS: editInstance.AddPlatform.OS, - Architecture: editInstance.AddPlatform.Architecture, - OSVersion: editInstance.AddPlatform.OSVersion, - OSFeatures: editInstance.AddPlatform.OSFeatures, - Variant: editInstance.AddPlatform.Variant, - }, + if editInstance.AddPlatform == nil { + // Should we create a struct with empty fields instead? + // Right now ListOpAdd is only called when an instance with the same platform value + // already exists in the manifest, so this should not be reached in practice. + return fmt.Errorf("adding a schema2 list instance with no platform specified is not supported") } - addedEntries = append(addedEntries, addInstance) + addedEntries = append(addedEntries, Schema2ManifestDescriptor{ + Schema2Descriptor{ + Digest: editInstance.AddDigest, + Size: editInstance.AddSize, + MediaType: editInstance.AddMediaType, + }, + schema2PlatformSpecFromOCIPlatform(*editInstance.AddPlatform), + }) default: return fmt.Errorf("internal error: invalid operation: %d", editInstance.ListOperation) } @@ -158,13 +156,7 @@ func (list *Schema2ListPublic) ChooseInstance(ctx *types.SystemContext) (digest. } for _, wantedPlatform := range wantedPlatforms { for _, d := range list.Manifests { - imagePlatform := imgspecv1.Platform{ - Architecture: d.Platform.Architecture, - OS: d.Platform.OS, - OSVersion: d.Platform.OSVersion, - OSFeatures: slices.Clone(d.Platform.OSFeatures), - Variant: d.Platform.Variant, - } + imagePlatform := ociPlatformFromSchema2PlatformSpec(d.Platform) if platform.MatchesPlatform(imagePlatform, wantedPlatform) { return d.Digest, nil } @@ -224,20 +216,14 @@ func Schema2ListPublicClone(list *Schema2ListPublic) *Schema2ListPublic { func (list *Schema2ListPublic) ToOCI1Index() (*OCI1IndexPublic, error) { components := make([]imgspecv1.Descriptor, 0, len(list.Manifests)) for _, manifest := range list.Manifests { - converted := imgspecv1.Descriptor{ + platform := ociPlatformFromSchema2PlatformSpec(manifest.Platform) + components = append(components, imgspecv1.Descriptor{ MediaType: manifest.MediaType, Size: manifest.Size, Digest: manifest.Digest, URLs: slices.Clone(manifest.URLs), - Platform: &imgspecv1.Platform{ - OS: manifest.Platform.OS, - Architecture: manifest.Platform.Architecture, - OSFeatures: slices.Clone(manifest.Platform.OSFeatures), - OSVersion: manifest.Platform.OSVersion, - Variant: manifest.Platform.Variant, - }, - } - components = append(components, converted) + Platform: &platform, + }) } oci := OCI1IndexPublicFromComponents(components, nil) return oci, nil @@ -312,3 +298,15 @@ func Schema2ListFromManifest(manifest []byte) (*Schema2List, error) { } return schema2ListFromPublic(public), nil } + +// ociPlatformFromSchema2PlatformSpec converts a schema2 platform p to the OCI struccture. +func ociPlatformFromSchema2PlatformSpec(p Schema2PlatformSpec) imgspecv1.Platform { + return imgspecv1.Platform{ + Architecture: p.Architecture, + OS: p.OS, + OSVersion: p.OSVersion, + OSFeatures: slices.Clone(p.OSFeatures), + Variant: p.Variant, + // Features is not supported in OCI, and discarded. + } +} diff --git a/vendor/github.com/containers/image/v5/internal/manifest/errors.go b/vendor/github.com/containers/image/v5/internal/manifest/errors.go index 6ebe4b24..6c8e233d 100644 --- a/vendor/github.com/containers/image/v5/internal/manifest/errors.go +++ b/vendor/github.com/containers/image/v5/internal/manifest/errors.go @@ -1,6 +1,10 @@ package manifest -import "fmt" +import ( + "fmt" + + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" +) // FIXME: This is a duplicate of c/image/manifestDockerV2Schema2ConfigMediaType. // Deduplicate that, depending on outcome of https://github.com/containers/image/pull/1791 . @@ -26,8 +30,20 @@ type NonImageArtifactError struct { mimeType string } -// NewNonImageArtifactError returns a NonImageArtifactError about an artifact with mimeType. -func NewNonImageArtifactError(mimeType string) error { +// NewNonImageArtifactError returns a NonImageArtifactError about an artifact manifest. +// +// This is typically called if manifest.Config.MediaType != imgspecv1.MediaTypeImageConfig . +func NewNonImageArtifactError(manifest *imgspecv1.Manifest) error { + // Callers decide based on manifest.Config.MediaType that this is not an image; + // in that case manifest.ArtifactType can be optionally defined, and if it is, it is typically + // more relevant because config may be ~absent with imgspecv1.MediaTypeEmptyJSON. + // + // If ArtifactType and Config.MediaType are both defined and non-trivial, presumably + // ArtifactType is the “top-level” one, although that’s not defined by the spec. + mimeType := manifest.ArtifactType + if mimeType == "" { + mimeType = manifest.Config.MediaType + } return NonImageArtifactError{mimeType: mimeType} } 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 8832caa3..dcd2646d 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 @@ -239,13 +239,7 @@ func (index *OCI1IndexPublic) chooseInstance(ctx *types.SystemContext, preferGzi for manifestIndex, d := range index.Manifests { candidate := instanceCandidate{platformIndex: math.MaxInt, manifestPosition: manifestIndex, isZstd: instanceIsZstd(d), digest: d.Digest} if d.Platform != nil { - imagePlatform := imgspecv1.Platform{ - Architecture: d.Platform.Architecture, - OS: d.Platform.OS, - OSVersion: d.Platform.OSVersion, - OSFeatures: slices.Clone(d.Platform.OSFeatures), - Variant: d.Platform.Variant, - } + imagePlatform := ociPlatformClone(*d.Platform) platformIndex := slices.IndexFunc(wantedPlatforms, func(wantedPlatform imgspecv1.Platform) bool { return platform.MatchesPlatform(imagePlatform, wantedPlatform) }) @@ -299,13 +293,8 @@ func OCI1IndexPublicFromComponents(components []imgspecv1.Descriptor, annotation for i, component := range components { var platform *imgspecv1.Platform if component.Platform != nil { - platform = &imgspecv1.Platform{ - Architecture: component.Platform.Architecture, - OS: component.Platform.OS, - OSVersion: component.Platform.OSVersion, - OSFeatures: slices.Clone(component.Platform.OSFeatures), - Variant: component.Platform.Variant, - } + platformCopy := ociPlatformClone(*component.Platform) + platform = &platformCopy } m := imgspecv1.Descriptor{ MediaType: component.MediaType, @@ -342,22 +331,15 @@ func (index *OCI1IndexPublic) ToSchema2List() (*Schema2ListPublic, error) { Architecture: runtime.GOARCH, } } - converted := Schema2ManifestDescriptor{ + components = append(components, Schema2ManifestDescriptor{ Schema2Descriptor{ MediaType: manifest.MediaType, Size: manifest.Size, Digest: manifest.Digest, URLs: slices.Clone(manifest.URLs), }, - Schema2PlatformSpec{ - OS: platform.OS, - Architecture: platform.Architecture, - OSFeatures: slices.Clone(platform.OSFeatures), - OSVersion: platform.OSVersion, - Variant: platform.Variant, - }, - } - components = append(components, converted) + schema2PlatformSpecFromOCIPlatform(*platform), + }) } s2 := Schema2ListPublicFromComponents(components) return s2, nil @@ -431,3 +413,32 @@ func OCI1IndexFromManifest(manifest []byte) (*OCI1Index, error) { } return oci1IndexFromPublic(public), nil } + +// ociPlatformClone returns an independent copy of p. +func ociPlatformClone(p imgspecv1.Platform) imgspecv1.Platform { + // The only practical way in Go to give read-only access to an array is to copy it. + // The only practical way in Go to copy a deep structure is to either do it manually field by field, + // or to use reflection (incl. a round-trip through JSON, which uses reflection). + // + // The combination of the two is just sad, and leads to code like this, which will + // need to be updated with every new Platform field. + return imgspecv1.Platform{ + Architecture: p.Architecture, + OS: p.OS, + OSVersion: p.OSVersion, + OSFeatures: slices.Clone(p.OSFeatures), + Variant: p.Variant, + } +} + +// schema2PlatformSpecFromOCIPlatform converts an OCI platform p to the schema2 structure. +func schema2PlatformSpecFromOCIPlatform(p imgspecv1.Platform) Schema2PlatformSpec { + return Schema2PlatformSpec{ + Architecture: p.Architecture, + OS: p.OS, + OSVersion: p.OSVersion, + OSFeatures: slices.Clone(p.OSFeatures), + Variant: p.Variant, + Features: nil, + } +} diff --git a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go index 59b1d4b9..3ba0e408 100644 --- a/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go +++ b/vendor/github.com/containers/image/v5/internal/pkg/platform/platform_matcher.go @@ -128,6 +128,10 @@ var compatibility = map[string][]string{ // the most compatible platform is first. // If some option (arch, os, variant) is not present, a value from current platform is detected. func WantedPlatforms(ctx *types.SystemContext) ([]imgspecv1.Platform, error) { + // Note that this does not use Platform.OSFeatures and Platform.OSVersion at all. + // The fields are not specified by the OCI specification, as of version 1.1, usefully enough + // to be interoperable, anyway. + wantedArch := runtime.GOARCH wantedVariant := "" if ctx != nil && ctx.ArchitectureChoice != "" { diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go index a70470d9..a85641c3 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci.go +++ b/vendor/github.com/containers/image/v5/manifest/oci.go @@ -202,7 +202,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type // Most software calling this without human intervention is going to expect the values to be realistic and relevant, // and is probably better served by failing; we can always re-visit that later if we fail now, but // if we started returning some data for OCI artifacts now, we couldn’t start failing in this function later. - return nil, manifest.NewNonImageArtifactError(m.Config.MediaType) + return nil, manifest.NewNonImageArtifactError(&m.Manifest) } config, err := configGetter(m.ConfigInfo()) @@ -253,7 +253,7 @@ func (m *OCI1) ImageID([]digest.Digest) (string, error) { // (The only known caller of ImageID is storage/storageImageDestination.computeID, // which can’t work with non-image artifacts.) if m.Config.MediaType != imgspecv1.MediaTypeImageConfig { - return "", manifest.NewNonImageArtifactError(m.Config.MediaType) + return "", manifest.NewNonImageArtifactError(&m.Manifest) } if err := m.Config.Digest.Validate(); err != nil { diff --git a/vendor/github.com/containers/image/v5/storage/storage_dest.go b/vendor/github.com/containers/image/v5/storage/storage_dest.go index da2d1f49..07e1d5e1 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_dest.go +++ b/vendor/github.com/containers/image/v5/storage/storage_dest.go @@ -57,7 +57,7 @@ type storageImageDestination struct { imageRef storageReference directory string // Temporary directory where we store blobs until Commit() time - nextTempFileID int32 // A counter that we use for computing filenames to assign to blobs + nextTempFileID atomic.Int32 // A counter that we use for computing filenames to assign to blobs manifest []byte // Manifest contents, temporary manifestDigest digest.Digest // Valid if len(manifest) != 0 signatures []byte // Signature contents, temporary @@ -154,7 +154,7 @@ func (s *storageImageDestination) Close() error { } func (s *storageImageDestination) computeNextBlobCacheFile() string { - return filepath.Join(s.directory, fmt.Sprintf("%d", atomic.AddInt32(&s.nextTempFileID, 1))) + return filepath.Join(s.directory, fmt.Sprintf("%d", s.nextTempFileID.Add(1))) } // PutBlobWithOptions writes contents of stream and returns data representing the result. @@ -763,7 +763,7 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t if len(layerBlobs) > 0 { // Can happen when using caches prev := s.indexToStorageID[len(layerBlobs)-1] if prev == nil { - return fmt.Errorf("Internal error: StorageImageDestination.Commit(): previous layer %d hasn't been committed (lastLayer == nil)", len(layerBlobs)-1) + return fmt.Errorf("Internal error: storageImageDestination.Commit(): previous layer %d hasn't been committed (lastLayer == nil)", len(layerBlobs)-1) } lastLayer = *prev } @@ -775,6 +775,78 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t logrus.Debugf("setting image creation date to %s", inspect.Created) options.CreationDate = *inspect.Created } + + // Set up to save the non-layer blobs as data items. Since we only share layers, they should all be in files, so + // we just need to screen out the ones that are actually layers to get the list of non-layers. + dataBlobs := set.New[digest.Digest]() + for blob := range s.filenames { + dataBlobs.Add(blob) + } + for _, layerBlob := range layerBlobs { + dataBlobs.Delete(layerBlob.Digest) + } + for _, blob := range dataBlobs.Values() { + v, err := os.ReadFile(s.filenames[blob]) + if err != nil { + return fmt.Errorf("copying non-layer blob %q to image: %w", blob, err) + } + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: blob.String(), + Data: v, + Digest: digest.Canonical.FromBytes(v), + }) + } + // Set up to save the unparsedToplevel's manifest if it differs from + // the per-platform one, which is saved below. + if len(toplevelManifest) != 0 && !bytes.Equal(toplevelManifest, s.manifest) { + manifestDigest, err := manifest.Digest(toplevelManifest) + if err != nil { + return fmt.Errorf("digesting top-level manifest: %w", err) + } + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: manifestBigDataKey(manifestDigest), + Data: toplevelManifest, + Digest: manifestDigest, + }) + } + // Set up to save the image's manifest. Allow looking it up by digest by using the key convention defined by the Store. + // Record the manifest twice: using a digest-specific key to allow references to that specific digest instance, + // and using storage.ImageDigestBigDataKey for future users that don’t specify any digest and for compatibility with older readers. + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: manifestBigDataKey(s.manifestDigest), + Data: s.manifest, + Digest: s.manifestDigest, + }) + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: storage.ImageDigestBigDataKey, + Data: s.manifest, + Digest: s.manifestDigest, + }) + // Set up to save the signatures, if we have any. + if len(s.signatures) > 0 { + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: "signatures", + Data: s.signatures, + Digest: digest.Canonical.FromBytes(s.signatures), + }) + } + for instanceDigest, signatures := range s.signatureses { + options.BigData = append(options.BigData, storage.ImageBigDataOption{ + Key: signatureBigDataKey(instanceDigest), + Data: signatures, + Digest: digest.Canonical.FromBytes(signatures), + }) + } + + // Set up to save our metadata. + metadata, err := json.Marshal(s) + if err != nil { + return fmt.Errorf("encoding metadata for image: %w", err) + } + if len(metadata) != 0 { + options.Metadata = string(metadata) + } + // Create the image record, pointing to the most-recently added layer. intendedID := s.imageRef.id if intendedID == "" { @@ -797,8 +869,26 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t } logrus.Debugf("reusing image ID %q", img.ID) oldNames = append(oldNames, img.Names...) + // set the data items and metadata on the already-present image + // FIXME: this _replaces_ any "signatures" blobs and their + // sizes (tracked in the metadata) which might have already + // been present with new values, when ideally we'd find a way + // to merge them since they all apply to the same image + for _, data := range options.BigData { + if err := s.imageRef.transport.store.SetImageBigData(img.ID, data.Key, data.Data, manifest.Digest); err != nil { + logrus.Debugf("error saving big data %q for image %q: %v", data.Key, img.ID, err) + return fmt.Errorf("saving big data %q for image %q: %w", data.Key, img.ID, err) + } + } + if options.Metadata != "" { + if err := s.imageRef.transport.store.SetMetadata(img.ID, options.Metadata); err != nil { + logrus.Debugf("error saving metadata for image %q: %v", img.ID, err) + return fmt.Errorf("saving metadata for image %q: %w", img.ID, err) + } + logrus.Debugf("saved image metadata %q", options.Metadata) + } } else { - logrus.Debugf("created new image ID %q", img.ID) + logrus.Debugf("created new image ID %q with metadata %q", img.ID, options.Metadata) } // Clean up the unfinished image on any error. @@ -813,78 +903,7 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t } }() - // Add the non-layer blobs as data items. Since we only share layers, they should all be in files, so - // we just need to screen out the ones that are actually layers to get the list of non-layers. - dataBlobs := set.New[digest.Digest]() - for blob := range s.filenames { - dataBlobs.Add(blob) - } - for _, layerBlob := range layerBlobs { - dataBlobs.Delete(layerBlob.Digest) - } - for _, blob := range dataBlobs.Values() { - v, err := os.ReadFile(s.filenames[blob]) - if err != nil { - return fmt.Errorf("copying non-layer blob %q to image: %w", blob, err) - } - if err := s.imageRef.transport.store.SetImageBigData(img.ID, blob.String(), v, manifest.Digest); err != nil { - logrus.Debugf("error saving big data %q for image %q: %v", blob.String(), img.ID, err) - return fmt.Errorf("saving big data %q for image %q: %w", blob.String(), img.ID, err) - } - } - // Save the unparsedToplevel's manifest if it differs from the per-platform one, which is saved below. - if len(toplevelManifest) != 0 && !bytes.Equal(toplevelManifest, s.manifest) { - manifestDigest, err := manifest.Digest(toplevelManifest) - if err != nil { - return fmt.Errorf("digesting top-level manifest: %w", err) - } - key := manifestBigDataKey(manifestDigest) - if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, toplevelManifest, manifest.Digest); err != nil { - logrus.Debugf("error saving top-level manifest for image %q: %v", img.ID, err) - return fmt.Errorf("saving top-level manifest for image %q: %w", img.ID, err) - } - } - // Save the image's manifest. Allow looking it up by digest by using the key convention defined by the Store. - // Record the manifest twice: using a digest-specific key to allow references to that specific digest instance, - // and using storage.ImageDigestBigDataKey for future users that don’t specify any digest and for compatibility with older readers. - key := manifestBigDataKey(s.manifestDigest) - if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, s.manifest, manifest.Digest); err != nil { - logrus.Debugf("error saving manifest for image %q: %v", img.ID, err) - return fmt.Errorf("saving manifest for image %q: %w", img.ID, err) - } - key = storage.ImageDigestBigDataKey - if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, s.manifest, manifest.Digest); err != nil { - logrus.Debugf("error saving manifest for image %q: %v", img.ID, err) - return fmt.Errorf("saving manifest for image %q: %w", img.ID, err) - } - // Save the signatures, if we have any. - if len(s.signatures) > 0 { - if err := s.imageRef.transport.store.SetImageBigData(img.ID, "signatures", s.signatures, manifest.Digest); err != nil { - logrus.Debugf("error saving signatures for image %q: %v", img.ID, err) - return fmt.Errorf("saving signatures for image %q: %w", img.ID, err) - } - } - for instanceDigest, signatures := range s.signatureses { - key := signatureBigDataKey(instanceDigest) - if err := s.imageRef.transport.store.SetImageBigData(img.ID, key, signatures, manifest.Digest); err != nil { - logrus.Debugf("error saving signatures for image %q: %v", img.ID, err) - return fmt.Errorf("saving signatures for image %q: %w", img.ID, err) - } - } - // Save our metadata. - metadata, err := json.Marshal(s) - if err != nil { - logrus.Debugf("error encoding metadata for image %q: %v", img.ID, err) - return fmt.Errorf("encoding metadata for image %q: %w", img.ID, err) - } - if len(metadata) != 0 { - if err = s.imageRef.transport.store.SetMetadata(img.ID, string(metadata)); err != nil { - logrus.Debugf("error saving metadata for image %q: %v", img.ID, err) - return fmt.Errorf("saving metadata for image %q: %w", img.ID, err) - } - logrus.Debugf("saved image metadata %q", string(metadata)) - } - // Adds the reference's name on the image. We don't need to worry about avoiding duplicate + // Add the reference's name on the image. We don't need to worry about avoiding duplicate // values because AddNames() will deduplicate the list that we pass to it. if name := s.imageRef.DockerReference(); name != nil { if err := s.imageRef.transport.store.AddNames(img.ID, []string{name.String()}); err != nil { diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/config.go b/vendor/github.com/google/go-containerregistry/pkg/v1/config.go new file mode 100644 index 00000000..960c93b5 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/config.go @@ -0,0 +1,151 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "encoding/json" + "io" + "time" +) + +// ConfigFile is the configuration file that holds the metadata describing +// how to launch a container. See: +// https://github.com/opencontainers/image-spec/blob/master/config.md +// +// docker_version and os.version are not part of the spec but included +// for backwards compatibility. +type ConfigFile struct { + Architecture string `json:"architecture"` + Author string `json:"author,omitempty"` + Container string `json:"container,omitempty"` + Created Time `json:"created,omitempty"` + DockerVersion string `json:"docker_version,omitempty"` + History []History `json:"history,omitempty"` + OS string `json:"os"` + RootFS RootFS `json:"rootfs"` + Config Config `json:"config"` + OSVersion string `json:"os.version,omitempty"` + Variant string `json:"variant,omitempty"` + OSFeatures []string `json:"os.features,omitempty"` +} + +// Platform attempts to generates a Platform from the ConfigFile fields. +func (cf *ConfigFile) Platform() *Platform { + if cf.OS == "" && cf.Architecture == "" && cf.OSVersion == "" && cf.Variant == "" && len(cf.OSFeatures) == 0 { + return nil + } + return &Platform{ + OS: cf.OS, + Architecture: cf.Architecture, + OSVersion: cf.OSVersion, + Variant: cf.Variant, + OSFeatures: cf.OSFeatures, + } +} + +// History is one entry of a list recording how this container image was built. +type History struct { + Author string `json:"author,omitempty"` + Created Time `json:"created,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + Comment string `json:"comment,omitempty"` + EmptyLayer bool `json:"empty_layer,omitempty"` +} + +// Time is a wrapper around time.Time to help with deep copying +type Time struct { + time.Time +} + +// DeepCopyInto creates a deep-copy of the Time value. The underlying time.Time +// type is effectively immutable in the time API, so it is safe to +// copy-by-assign, despite the presence of (unexported) Pointer fields. +func (t *Time) DeepCopyInto(out *Time) { + *out = *t +} + +// RootFS holds the ordered list of file system deltas that comprise the +// container image's root filesystem. +type RootFS struct { + Type string `json:"type"` + DiffIDs []Hash `json:"diff_ids"` +} + +// HealthConfig holds configuration settings for the HEALTHCHECK feature. +type HealthConfig struct { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test []string `json:",omitempty"` + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. + Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. + StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries int `json:",omitempty"` +} + +// Config is a submessage of the config file described as: +// +// The execution parameters which SHOULD be used as a base when running +// a container using the image. +// +// The names of the fields in this message are chosen to reflect the JSON +// payload of the Config as defined here: +// https://git.io/vrAET +// and +// https://github.com/opencontainers/image-spec/blob/master/config.md +type Config struct { + AttachStderr bool `json:"AttachStderr,omitempty"` + AttachStdin bool `json:"AttachStdin,omitempty"` + AttachStdout bool `json:"AttachStdout,omitempty"` + Cmd []string `json:"Cmd,omitempty"` + Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` + Domainname string `json:"Domainname,omitempty"` + Entrypoint []string `json:"Entrypoint,omitempty"` + Env []string `json:"Env,omitempty"` + Hostname string `json:"Hostname,omitempty"` + Image string `json:"Image,omitempty"` + Labels map[string]string `json:"Labels,omitempty"` + OnBuild []string `json:"OnBuild,omitempty"` + OpenStdin bool `json:"OpenStdin,omitempty"` + StdinOnce bool `json:"StdinOnce,omitempty"` + Tty bool `json:"Tty,omitempty"` + User string `json:"User,omitempty"` + Volumes map[string]struct{} `json:"Volumes,omitempty"` + WorkingDir string `json:"WorkingDir,omitempty"` + ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` + ArgsEscaped bool `json:"ArgsEscaped,omitempty"` + NetworkDisabled bool `json:"NetworkDisabled,omitempty"` + MacAddress string `json:"MacAddress,omitempty"` + StopSignal string `json:"StopSignal,omitempty"` + Shell []string `json:"Shell,omitempty"` +} + +// ParseConfigFile parses the io.Reader's contents into a ConfigFile. +func ParseConfigFile(r io.Reader) (*ConfigFile, error) { + cf := ConfigFile{} + if err := json.NewDecoder(r).Decode(&cf); err != nil { + return nil, err + } + return &cf, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go b/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go new file mode 100644 index 00000000..7a84736b --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/doc.go @@ -0,0 +1,18 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// +k8s:deepcopy-gen=package + +// Package v1 defines structured types for OCI v1 images +package v1 diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go b/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go new file mode 100644 index 00000000..f78a5fa8 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/hash.go @@ -0,0 +1,123 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "crypto" + "encoding/hex" + "encoding/json" + "fmt" + "hash" + "io" + "strconv" + "strings" +) + +// Hash is an unqualified digest of some content, e.g. sha256:deadbeef +type Hash struct { + // Algorithm holds the algorithm used to compute the hash. + Algorithm string + + // Hex holds the hex portion of the content hash. + Hex string +} + +// String reverses NewHash returning the string-form of the hash. +func (h Hash) String() string { + return fmt.Sprintf("%s:%s", h.Algorithm, h.Hex) +} + +// NewHash validates the input string is a hash and returns a strongly type Hash object. +func NewHash(s string) (Hash, error) { + h := Hash{} + if err := h.parse(s); err != nil { + return Hash{}, err + } + return h, nil +} + +// MarshalJSON implements json.Marshaler +func (h Hash) MarshalJSON() ([]byte, error) { + return json.Marshal(h.String()) +} + +// UnmarshalJSON implements json.Unmarshaler +func (h *Hash) UnmarshalJSON(data []byte) error { + s, err := strconv.Unquote(string(data)) + if err != nil { + return err + } + return h.parse(s) +} + +// MarshalText implements encoding.TextMarshaler. This is required to use +// v1.Hash as a key in a map when marshalling JSON. +func (h Hash) MarshalText() (text []byte, err error) { + return []byte(h.String()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This is required to use +// v1.Hash as a key in a map when unmarshalling JSON. +func (h *Hash) UnmarshalText(text []byte) error { + return h.parse(string(text)) +} + +// Hasher returns a hash.Hash for the named algorithm (e.g. "sha256") +func Hasher(name string) (hash.Hash, error) { + switch name { + case "sha256": + return crypto.SHA256.New(), nil + default: + return nil, fmt.Errorf("unsupported hash: %q", name) + } +} + +func (h *Hash) parse(unquoted string) error { + parts := strings.Split(unquoted, ":") + if len(parts) != 2 { + return fmt.Errorf("cannot parse hash: %q", unquoted) + } + + rest := strings.TrimLeft(parts[1], "0123456789abcdef") + if len(rest) != 0 { + return fmt.Errorf("found non-hex character in hash: %c", rest[0]) + } + + hasher, err := Hasher(parts[0]) + if err != nil { + return err + } + // Compare the hex to the expected size (2 hex characters per byte) + if len(parts[1]) != hasher.Size()*2 { + return fmt.Errorf("wrong number of hex digits for %s: %s", parts[0], parts[1]) + } + + h.Algorithm = parts[0] + h.Hex = parts[1] + return nil +} + +// SHA256 computes the Hash of the provided io.Reader's content. +func SHA256(r io.Reader) (Hash, int64, error) { + hasher := crypto.SHA256.New() + n, err := io.Copy(hasher, r) + if err != nil { + return Hash{}, 0, err + } + return Hash{ + Algorithm: "sha256", + Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), + }, n, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/image.go b/vendor/github.com/google/go-containerregistry/pkg/v1/image.go new file mode 100644 index 00000000..8de9e476 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/image.go @@ -0,0 +1,59 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Image defines the interface for interacting with an OCI v1 image. +type Image interface { + // Layers returns the ordered collection of filesystem layers that comprise this image. + // The order of the list is oldest/base layer first, and most-recent/top layer last. + Layers() ([]Layer, error) + + // MediaType of this image's manifest. + MediaType() (types.MediaType, error) + + // Size returns the size of the manifest. + Size() (int64, error) + + // ConfigName returns the hash of the image's config file, also known as + // the Image ID. + ConfigName() (Hash, error) + + // ConfigFile returns this image's config file. + ConfigFile() (*ConfigFile, error) + + // RawConfigFile returns the serialized bytes of ConfigFile(). + RawConfigFile() ([]byte, error) + + // Digest returns the sha256 of this image's manifest. + Digest() (Hash, error) + + // Manifest returns this image's Manifest object. + Manifest() (*Manifest, error) + + // RawManifest returns the serialized bytes of Manifest() + RawManifest() ([]byte, error) + + // LayerByDigest returns a Layer for interacting with a particular layer of + // the image, looking it up by "digest" (the compressed hash). + LayerByDigest(Hash) (Layer, error) + + // LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" + // (the uncompressed hash). + LayerByDiffID(Hash) (Layer, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/index.go b/vendor/github.com/google/go-containerregistry/pkg/v1/index.go new file mode 100644 index 00000000..8e7bc8eb --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/index.go @@ -0,0 +1,43 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// ImageIndex defines the interface for interacting with an OCI image index. +type ImageIndex interface { + // MediaType of this image's manifest. + MediaType() (types.MediaType, error) + + // Digest returns the sha256 of this index's manifest. + Digest() (Hash, error) + + // Size returns the size of the manifest. + Size() (int64, error) + + // IndexManifest returns this image index's manifest object. + IndexManifest() (*IndexManifest, error) + + // RawManifest returns the serialized bytes of IndexManifest(). + RawManifest() ([]byte, error) + + // Image returns a v1.Image that this ImageIndex references. + Image(Hash) (Image, error) + + // ImageIndex returns a v1.ImageIndex that this ImageIndex references. + ImageIndex(Hash) (ImageIndex, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go b/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go new file mode 100644 index 00000000..57447d26 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/layer.go @@ -0,0 +1,42 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "io" + + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Layer is an interface for accessing the properties of a particular layer of a v1.Image +type Layer interface { + // Digest returns the Hash of the compressed layer. + Digest() (Hash, error) + + // DiffID returns the Hash of the uncompressed layer. + DiffID() (Hash, error) + + // Compressed returns an io.ReadCloser for the compressed layer contents. + Compressed() (io.ReadCloser, error) + + // Uncompressed returns an io.ReadCloser for the uncompressed layer contents. + Uncompressed() (io.ReadCloser, error) + + // Size returns the compressed size of the Layer. + Size() (int64, error) + + // MediaType returns the media type of the Layer. + MediaType() (types.MediaType, error) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go b/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go new file mode 100644 index 00000000..22d483f3 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go @@ -0,0 +1,71 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "encoding/json" + "io" + + "github.com/google/go-containerregistry/pkg/v1/types" +) + +// Manifest represents the OCI image manifest in a structured way. +type Manifest struct { + SchemaVersion int64 `json:"schemaVersion"` + MediaType types.MediaType `json:"mediaType,omitempty"` + Config Descriptor `json:"config"` + Layers []Descriptor `json:"layers"` + Annotations map[string]string `json:"annotations,omitempty"` + Subject *Descriptor `json:"subject,omitempty"` +} + +// IndexManifest represents an OCI image index in a structured way. +type IndexManifest struct { + SchemaVersion int64 `json:"schemaVersion"` + MediaType types.MediaType `json:"mediaType,omitempty"` + Manifests []Descriptor `json:"manifests"` + Annotations map[string]string `json:"annotations,omitempty"` + Subject *Descriptor `json:"subject,omitempty"` +} + +// Descriptor holds a reference from the manifest to one of its constituent elements. +type Descriptor struct { + MediaType types.MediaType `json:"mediaType"` + Size int64 `json:"size"` + Digest Hash `json:"digest"` + Data []byte `json:"data,omitempty"` + URLs []string `json:"urls,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + Platform *Platform `json:"platform,omitempty"` + ArtifactType string `json:"artifactType,omitempty"` +} + +// ParseManifest parses the io.Reader's contents into a Manifest. +func ParseManifest(r io.Reader) (*Manifest, error) { + m := Manifest{} + if err := json.NewDecoder(r).Decode(&m); err != nil { + return nil, err + } + return &m, nil +} + +// ParseIndexManifest parses the io.Reader's contents into an IndexManifest. +func ParseIndexManifest(r io.Reader) (*IndexManifest, error) { + im := IndexManifest{} + if err := json.NewDecoder(r).Decode(&im); err != nil { + return nil, err + } + return &im, nil +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go b/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go new file mode 100644 index 00000000..59ca4026 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/platform.go @@ -0,0 +1,149 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 v1 + +import ( + "fmt" + "sort" + "strings" +) + +// Platform represents the target os/arch for an image. +type Platform struct { + Architecture string `json:"architecture"` + OS string `json:"os"` + OSVersion string `json:"os.version,omitempty"` + OSFeatures []string `json:"os.features,omitempty"` + Variant string `json:"variant,omitempty"` + Features []string `json:"features,omitempty"` +} + +func (p Platform) String() string { + if p.OS == "" { + return "" + } + var b strings.Builder + b.WriteString(p.OS) + if p.Architecture != "" { + b.WriteString("/") + b.WriteString(p.Architecture) + } + if p.Variant != "" { + b.WriteString("/") + b.WriteString(p.Variant) + } + if p.OSVersion != "" { + b.WriteString(":") + b.WriteString(p.OSVersion) + } + return b.String() +} + +// ParsePlatform parses a string representing a Platform, if possible. +func ParsePlatform(s string) (*Platform, error) { + var p Platform + parts := strings.Split(strings.TrimSpace(s), ":") + if len(parts) == 2 { + p.OSVersion = parts[1] + } + parts = strings.Split(parts[0], "/") + if len(parts) > 0 { + p.OS = parts[0] + } + if len(parts) > 1 { + p.Architecture = parts[1] + } + if len(parts) > 2 { + p.Variant = parts[2] + } + if len(parts) > 3 { + return nil, fmt.Errorf("too many slashes in platform spec: %s", s) + } + return &p, nil +} + +// Equals returns true if the given platform is semantically equivalent to this one. +// The order of Features and OSFeatures is not important. +func (p Platform) Equals(o Platform) bool { + return p.OS == o.OS && + p.Architecture == o.Architecture && + p.Variant == o.Variant && + p.OSVersion == o.OSVersion && + stringSliceEqualIgnoreOrder(p.OSFeatures, o.OSFeatures) && + stringSliceEqualIgnoreOrder(p.Features, o.Features) +} + +// Satisfies returns true if this Platform "satisfies" the given spec Platform. +// +// Note that this is different from Equals and that Satisfies is not reflexive. +// +// The given spec represents "requirements" such that any missing values in the +// spec are not compared. +// +// For OSFeatures and Features, Satisfies will return true if this Platform's +// fields contain a superset of the values in the spec's fields (order ignored). +func (p Platform) Satisfies(spec Platform) bool { + return satisfies(spec.OS, p.OS) && + satisfies(spec.Architecture, p.Architecture) && + satisfies(spec.Variant, p.Variant) && + satisfies(spec.OSVersion, p.OSVersion) && + satisfiesList(spec.OSFeatures, p.OSFeatures) && + satisfiesList(spec.Features, p.Features) +} + +func satisfies(want, have string) bool { + return want == "" || want == have +} + +func satisfiesList(want, have []string) bool { + if len(want) == 0 { + return true + } + + set := map[string]struct{}{} + for _, h := range have { + set[h] = struct{}{} + } + + for _, w := range want { + if _, ok := set[w]; !ok { + return false + } + } + + return true +} + +// stringSliceEqual compares 2 string slices and returns if their contents are identical. +func stringSliceEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, elm := range a { + if elm != b[i] { + return false + } + } + return true +} + +// stringSliceEqualIgnoreOrder compares 2 string slices and returns if their contents are identical, ignoring order +func stringSliceEqualIgnoreOrder(a, b []string) bool { + if a != nil && b != nil { + sort.Strings(a) + sort.Strings(b) + } + return stringSliceEqual(a, b) +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go b/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go new file mode 100644 index 00000000..844f04d9 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/progress.go @@ -0,0 +1,25 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// 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 v1 + +// Update representation of an update of transfer progress. Some functions +// in this module can take a channel to which updates will be sent while a +// transfer is in progress. +// +k8s:deepcopy-gen=false +type Update struct { + Total int64 + Complete int64 + Error error +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go b/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go new file mode 100644 index 00000000..c86657d7 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go @@ -0,0 +1,98 @@ +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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 types holds common OCI media types. +package types + +// MediaType is an enumeration of the supported mime types that an element of an image might have. +type MediaType string + +// The collection of known MediaType values. +const ( + OCIContentDescriptor MediaType = "application/vnd.oci.descriptor.v1+json" + OCIImageIndex MediaType = "application/vnd.oci.image.index.v1+json" + OCIManifestSchema1 MediaType = "application/vnd.oci.image.manifest.v1+json" + OCIConfigJSON MediaType = "application/vnd.oci.image.config.v1+json" + OCILayer MediaType = "application/vnd.oci.image.layer.v1.tar+gzip" + OCILayerZStd MediaType = "application/vnd.oci.image.layer.v1.tar+zstd" + OCIRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" + OCIUncompressedLayer MediaType = "application/vnd.oci.image.layer.v1.tar" + OCIUncompressedRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar" + + DockerManifestSchema1 MediaType = "application/vnd.docker.distribution.manifest.v1+json" + DockerManifestSchema1Signed MediaType = "application/vnd.docker.distribution.manifest.v1+prettyjws" + DockerManifestSchema2 MediaType = "application/vnd.docker.distribution.manifest.v2+json" + DockerManifestList MediaType = "application/vnd.docker.distribution.manifest.list.v2+json" + DockerLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar.gzip" + DockerConfigJSON MediaType = "application/vnd.docker.container.image.v1+json" + DockerPluginConfig MediaType = "application/vnd.docker.plugin.v1+json" + DockerForeignLayer MediaType = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" + DockerUncompressedLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar" + + OCIVendorPrefix = "vnd.oci" + DockerVendorPrefix = "vnd.docker" +) + +// IsDistributable returns true if a layer is distributable, see: +// https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers +func (m MediaType) IsDistributable() bool { + switch m { + case DockerForeignLayer, OCIRestrictedLayer, OCIUncompressedRestrictedLayer: + return false + } + return true +} + +// IsImage returns true if the mediaType represents an image manifest, as opposed to something else, like an index. +func (m MediaType) IsImage() bool { + switch m { + case OCIManifestSchema1, DockerManifestSchema2: + return true + } + return false +} + +// IsIndex returns true if the mediaType represents an index, as opposed to something else, like an image. +func (m MediaType) IsIndex() bool { + switch m { + case OCIImageIndex, DockerManifestList: + return true + } + return false +} + +// IsConfig returns true if the mediaType represents a config, as opposed to something else, like an image. +func (m MediaType) IsConfig() bool { + switch m { + case OCIConfigJSON, DockerConfigJSON: + return true + } + return false +} + +func (m MediaType) IsSchema1() bool { + switch m { + case DockerManifestSchema1, DockerManifestSchema1Signed: + return true + } + return false +} + +func (m MediaType) IsLayer() bool { + switch m { + case DockerLayer, DockerUncompressedLayer, OCILayer, OCILayerZStd, OCIUncompressedLayer, DockerForeignLayer, OCIRestrictedLayer, OCIUncompressedRestrictedLayer: + return true + } + return false +} diff --git a/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go b/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go new file mode 100644 index 00000000..a47b7475 --- /dev/null +++ b/vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go @@ -0,0 +1,339 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Copyright 2018 Google LLC All Rights Reserved. +// +// 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. + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + if in.Cmd != nil { + in, out := &in.Cmd, &out.Cmd + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Healthcheck != nil { + in, out := &in.Healthcheck, &out.Healthcheck + *out = new(HealthConfig) + (*in).DeepCopyInto(*out) + } + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.OnBuild != nil { + in, out := &in.OnBuild, &out.OnBuild + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make(map[string]struct{}, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ExposedPorts != nil { + in, out := &in.ExposedPorts, &out.ExposedPorts + *out = make(map[string]struct{}, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Shell != nil { + in, out := &in.Shell, &out.Shell + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigFile) DeepCopyInto(out *ConfigFile) { + *out = *in + in.Created.DeepCopyInto(&out.Created) + if in.History != nil { + in, out := &in.History, &out.History + *out = make([]History, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.RootFS.DeepCopyInto(&out.RootFS) + in.Config.DeepCopyInto(&out.Config) + if in.OSFeatures != nil { + in, out := &in.OSFeatures, &out.OSFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigFile. +func (in *ConfigFile) DeepCopy() *ConfigFile { + if in == nil { + return nil + } + out := new(ConfigFile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Descriptor) DeepCopyInto(out *Descriptor) { + *out = *in + out.Digest = in.Digest + if in.Data != nil { + in, out := &in.Data, &out.Data + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.URLs != nil { + in, out := &in.URLs, &out.URLs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Platform != nil { + in, out := &in.Platform, &out.Platform + *out = new(Platform) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Descriptor. +func (in *Descriptor) DeepCopy() *Descriptor { + if in == nil { + return nil + } + out := new(Descriptor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Hash) DeepCopyInto(out *Hash) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hash. +func (in *Hash) DeepCopy() *Hash { + if in == nil { + return nil + } + out := new(Hash) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthConfig) DeepCopyInto(out *HealthConfig) { + *out = *in + if in.Test != nil { + in, out := &in.Test, &out.Test + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthConfig. +func (in *HealthConfig) DeepCopy() *HealthConfig { + if in == nil { + return nil + } + out := new(HealthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *History) DeepCopyInto(out *History) { + *out = *in + in.Created.DeepCopyInto(&out.Created) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new History. +func (in *History) DeepCopy() *History { + if in == nil { + return nil + } + out := new(History) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IndexManifest) DeepCopyInto(out *IndexManifest) { + *out = *in + if in.Manifests != nil { + in, out := &in.Manifests, &out.Manifests + *out = make([]Descriptor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Subject != nil { + in, out := &in.Subject, &out.Subject + *out = new(Descriptor) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IndexManifest. +func (in *IndexManifest) DeepCopy() *IndexManifest { + if in == nil { + return nil + } + out := new(IndexManifest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Manifest) DeepCopyInto(out *Manifest) { + *out = *in + in.Config.DeepCopyInto(&out.Config) + if in.Layers != nil { + in, out := &in.Layers, &out.Layers + *out = make([]Descriptor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Subject != nil { + in, out := &in.Subject, &out.Subject + *out = new(Descriptor) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Manifest. +func (in *Manifest) DeepCopy() *Manifest { + if in == nil { + return nil + } + out := new(Manifest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Platform) DeepCopyInto(out *Platform) { + *out = *in + if in.OSFeatures != nil { + in, out := &in.OSFeatures, &out.OSFeatures + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Features != nil { + in, out := &in.Features, &out.Features + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. +func (in *Platform) DeepCopy() *Platform { + if in == nil { + return nil + } + out := new(Platform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RootFS) DeepCopyInto(out *RootFS) { + *out = *in + if in.DiffIDs != nil { + in, out := &in.DiffIDs, &out.DiffIDs + *out = make([]Hash, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootFS. +func (in *RootFS) DeepCopy() *RootFS { + if in == nil { + return nil + } + out := new(RootFS) + in.DeepCopyInto(out) + return out +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Time. +func (in *Time) DeepCopy() *Time { + if in == nil { + return nil + } + out := new(Time) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor.go b/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor.go index 19272824..3b7a4776 100644 --- a/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor.go +++ b/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor.go @@ -10,13 +10,18 @@ package sif import ( "bytes" "crypto" + "crypto/sha256" "encoding" "encoding/binary" + "encoding/hex" "errors" "fmt" + "hash" "io" "strings" "time" + + v1 "github.com/google/go-containerregistry/pkg/v1" ) // rawDescriptor represents an on-disk object descriptor. @@ -67,6 +72,40 @@ type sbom struct { Format SBOMFormat } +// ociBlob represents the OCI Blob data object descriptor. +type ociBlob struct { + hasher hash.Hash // accumulates hash while writing blob. + digest v1.Hash +} + +// newOCIBlobDigest returns a new ociBlob, that accumulates the digest of an OCI blob as it is +// read. The caller should take care to ensure that the entire contents of the blob have been +// written to the returned ociBlob prior to calling MarshalBinary. +func newOCIBlobDigest() *ociBlob { + return &ociBlob{ + hasher: sha256.New(), + digest: v1.Hash{ + Algorithm: "sha256", + }, + } +} + +// MarshalBinary encodes ob into binary format. +func (ob *ociBlob) MarshalBinary() ([]byte, error) { + ob.digest.Hex = hex.EncodeToString(ob.hasher.Sum(nil)) + + return ob.digest.MarshalText() +} + +// UnmarshalBinary decodes b into ob. +func (ob *ociBlob) UnmarshalBinary(b []byte) error { + if before, _, ok := bytes.Cut(b, []byte{0x00}); ok { + b = before + } + + return ob.digest.UnmarshalText(b) +} + // The binaryMarshaler type is an adapter that allows a type suitable for use with the // encoding/binary package to be used as an encoding.BinaryMarshaler. type binaryMarshaler struct{ any } @@ -295,6 +334,21 @@ func (d Descriptor) SBOMMetadata() (SBOMFormat, error) { return s.Format, nil } +// OCIBlobDigest returns the digest for a OCI blob object. +func (d Descriptor) OCIBlobDigest() (v1.Hash, error) { + if got := d.raw.DataType; got != DataOCIRootIndex && got != DataOCIBlob { + return v1.Hash{}, &unexpectedDataTypeError{got, []DataType{DataOCIRootIndex, DataOCIBlob}} + } + + var o ociBlob + + if err := d.raw.getExtra(&o); err != nil { + return v1.Hash{}, fmt.Errorf("%w", err) + } + + return o.digest, nil +} + // GetData returns the data object associated with descriptor d. func (d Descriptor) GetData() ([]byte, error) { b := make([]byte, d.raw.Size) diff --git a/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor_input.go b/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor_input.go index 3cfe5c65..4994ff11 100644 --- a/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor_input.go +++ b/vendor/github.com/sylabs/sif/v2/pkg/sif/descriptor_input.go @@ -293,6 +293,15 @@ func NewDescriptorInput(t DataType, r io.Reader, opts ...DescriptorInputOpt) (De dopts.alignment = 4096 } + // Accumulate hash for OCI blobs as they are written. + if t == DataOCIRootIndex || t == DataOCIBlob { + md := newOCIBlobDigest() + + r = io.TeeReader(r, md.hasher) + + dopts.md = md + } + for _, opt := range opts { if err := opt(t, &dopts); err != nil { return DescriptorInput{}, fmt.Errorf("%w", err) diff --git a/vendor/github.com/sylabs/sif/v2/pkg/sif/select.go b/vendor/github.com/sylabs/sif/v2/pkg/sif/select.go index 635d6e89..ee7892f7 100644 --- a/vendor/github.com/sylabs/sif/v2/pkg/sif/select.go +++ b/vendor/github.com/sylabs/sif/v2/pkg/sif/select.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021, Sylabs Inc. All rights reserved. +// Copyright (c) 2021-2023, Sylabs Inc. All rights reserved. // This software is licensed under a 3-clause BSD license. Please consult the // LICENSE file distributed with the sources of this project regarding your // rights to use or distribute this software. @@ -8,6 +8,8 @@ package sif import ( "errors" "fmt" + + v1 "github.com/google/go-containerregistry/pkg/v1" ) // ErrNoObjects is the error returned when an image contains no data objects. @@ -92,6 +94,16 @@ func WithPartitionType(pt PartType) DescriptorSelectorFunc { } } +// WithOCIBlobDigest selects descriptors that contain a OCI blob with the specified digest. +func WithOCIBlobDigest(digest v1.Hash) DescriptorSelectorFunc { + return func(d Descriptor) (bool, error) { + if h, err := d.OCIBlobDigest(); err == nil { + return h.String() == digest.String(), nil + } + return false, nil + } +} + // descriptorFromRaw populates a Descriptor from rd. func (f *FileImage) descriptorFromRaw(rd *rawDescriptor) Descriptor { return Descriptor{ diff --git a/vendor/github.com/sylabs/sif/v2/pkg/sif/sif.go b/vendor/github.com/sylabs/sif/v2/pkg/sif/sif.go index 74ff1007..bf39dfe4 100644 --- a/vendor/github.com/sylabs/sif/v2/pkg/sif/sif.go +++ b/vendor/github.com/sylabs/sif/v2/pkg/sif/sif.go @@ -133,6 +133,8 @@ const ( DataGeneric // generic / raw data DataCryptoMessage // cryptographic message data object DataSBOM // software bill of materials + DataOCIRootIndex // root OCI index + DataOCIBlob // oci blob data object ) // String returns a human-readable representation of t. @@ -156,6 +158,10 @@ func (t DataType) String() string { return "Cryptographic Message" case DataSBOM: return "SBOM" + case DataOCIRootIndex: + return "OCI.RootIndex" + case DataOCIBlob: + return "OCI.Blob" } return "Unknown" } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index b9632380..b0d482f9 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -19,6 +19,7 @@ import ( "io/fs" "log" "math" + "math/bits" mathrand "math/rand" "net" "net/http" @@ -518,11 +519,14 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { func authorityAddr(scheme string, authority string) (addr string) { host, port, err := net.SplitHostPort(authority) if err != nil { // authority didn't have a port + host = authority + port = "" + } + if port == "" { // authority's port was empty port = "443" if scheme == "http" { port = "80" } - host = authority } if a, err := idna.ToASCII(host); err == nil { host = a @@ -1677,7 +1681,27 @@ func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int { return int(n) // doesn't truncate; max is 512K } -var bufPool sync.Pool // of *[]byte +// Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running +// streaming requests using small frame sizes occupy large buffers initially allocated for prior +// requests needing big buffers. The size ranges are as follows: +// {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB], +// {256 KB, 512 KB], {512 KB, infinity} +// In practice, the maximum scratch buffer size should not exceed 512 KB due to +// frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used. +// It exists mainly as a safety measure, for potential future increases in max buffer size. +var bufPools [7]sync.Pool // of *[]byte +func bufPoolIndex(size int) int { + if size <= 16384 { + return 0 + } + size -= 1 + bits := bits.Len(uint(size)) + index := bits - 14 + if index >= len(bufPools) { + return len(bufPools) - 1 + } + return index +} func (cs *clientStream) writeRequestBody(req *http.Request) (err error) { cc := cs.cc @@ -1695,12 +1719,13 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) { // Scratch buffer for reading into & writing from. scratchLen := cs.frameScratchBufferLen(maxFrameSize) var buf []byte - if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen { - defer bufPool.Put(bp) + index := bufPoolIndex(scratchLen) + if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen { + defer bufPools[index].Put(bp) buf = *bp } else { buf = make([]byte, scratchLen) - defer bufPool.Put(&buf) + defer bufPools[index].Put(&buf) } var sawEOF bool diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go index e1755d1d..d28140f7 100644 --- a/vendor/golang.org/x/oauth2/internal/client_appengine.go +++ b/vendor/golang.org/x/oauth2/internal/client_appengine.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build appengine -// +build appengine package internal diff --git a/vendor/modules.txt b/vendor/modules.txt index b68f639e..f8f87d39 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -70,8 +70,8 @@ github.com/containers/common/pkg/flag github.com/containers/common/pkg/report github.com/containers/common/pkg/report/camelcase github.com/containers/common/pkg/retry -# github.com/containers/image/v5 v5.26.1-0.20230802064408-aca060028898 -## explicit; go 1.18 +# github.com/containers/image/v5 v5.27.1-0.20230814071742-35192da58823 +## explicit; go 1.19 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath @@ -333,9 +333,11 @@ github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp -# github.com/google/go-containerregistry v0.15.2 +# github.com/google/go-containerregistry v0.16.1 ## explicit; go 1.18 github.com/google/go-containerregistry/pkg/name +github.com/google/go-containerregistry/pkg/v1 +github.com/google/go-containerregistry/pkg/v1/types # github.com/google/go-intervals v0.0.2 ## explicit; go 1.12 github.com/google/go-intervals/intervalset @@ -482,7 +484,7 @@ github.com/sigstore/rekor/pkg/generated/client/pubkey github.com/sigstore/rekor/pkg/generated/client/tlog github.com/sigstore/rekor/pkg/generated/models github.com/sigstore/rekor/pkg/util -# github.com/sigstore/sigstore v1.7.1 +# github.com/sigstore/sigstore v1.7.2 ## explicit; go 1.19 github.com/sigstore/sigstore/pkg/cryptoutils github.com/sigstore/sigstore/pkg/oauth @@ -510,7 +512,7 @@ github.com/stefanberger/go-pkcs11uri github.com/stretchr/testify/assert github.com/stretchr/testify/require github.com/stretchr/testify/suite -# github.com/sylabs/sif/v2 v2.11.5 +# github.com/sylabs/sif/v2 v2.12.0 ## explicit; go 1.19 github.com/sylabs/sif/v2/pkg/sif # github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 @@ -593,7 +595,7 @@ go.opentelemetry.io/otel/metric/embedded # go.opentelemetry.io/otel/trace v1.16.0 ## explicit; go 1.19 go.opentelemetry.io/otel/trace -# golang.org/x/crypto v0.11.0 +# golang.org/x/crypto v0.12.0 ## explicit; go 1.17 golang.org/x/crypto/cast5 golang.org/x/crypto/ed25519 @@ -620,7 +622,7 @@ golang.org/x/exp/slices ## explicit; go 1.17 golang.org/x/mod/semver golang.org/x/mod/sumdb/note -# golang.org/x/net v0.12.0 +# golang.org/x/net v0.14.0 ## explicit; go 1.17 golang.org/x/net/context golang.org/x/net/http/httpguts @@ -631,8 +633,8 @@ golang.org/x/net/internal/socks golang.org/x/net/internal/timeseries golang.org/x/net/proxy golang.org/x/net/trace -# golang.org/x/oauth2 v0.10.0 -## explicit; go 1.17 +# golang.org/x/oauth2 v0.11.0 +## explicit; go 1.18 golang.org/x/oauth2 golang.org/x/oauth2/internal # golang.org/x/sync v0.3.0 @@ -651,7 +653,7 @@ golang.org/x/sys/windows/registry # golang.org/x/term v0.11.0 ## explicit; go 1.17 golang.org/x/term -# golang.org/x/text v0.11.0 +# golang.org/x/text v0.12.0 ## explicit; go 1.17 golang.org/x/text/secure/bidirule golang.org/x/text/transform