diff --git a/cmd/podman/common/build.go b/cmd/podman/common/build.go index 033ba8e9ae..e019a52914 100644 --- a/cmd/podman/common/build.go +++ b/cmd/podman/common/build.go @@ -54,7 +54,7 @@ type BuildFlagsWrapper struct { // supported or don't make sense in the farm build use case var FarmBuildHiddenFlags = []string{ "arch", "all-platforms", "compress", "cw", "disable-content-trust", - "logsplit", "manifest", "os", "output", "platform", "sign-by", "signature-policy", "stdin", + "logsplit", "manifest", "metadata-file", "os", "output", "platform", "sign-by", "signature-policy", "stdin", "variant", } @@ -149,6 +149,9 @@ func ParseBuildOpts(cmd *cobra.Command, args []string, buildOpts *BuildFlagsWrap if cmd.Flag("output").Changed && registry.IsRemote() { return nil, errors.New("'--output' option is not supported in remote mode") } + if cmd.Flag("metadata-file").Changed && registry.IsRemote() { + return nil, errors.New("'--metadata-file' option is not supported in remote mode") + } if buildOpts.Network == "none" { if cmd.Flag("dns").Changed { @@ -612,6 +615,7 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *Buil UnsetEnvs: flags.UnsetEnvs, UnsetLabels: flags.UnsetLabels, UnsetAnnotations: flags.UnsetAnnotations, + MetadataFile: flags.MetadataFile, } if c.Flag("created-annotation").Changed { diff --git a/cmd/podman/farm/build.go b/cmd/podman/farm/build.go index cfe317b84a..c70e37d3ac 100644 --- a/cmd/podman/farm/build.go +++ b/cmd/podman/farm/build.go @@ -109,6 +109,11 @@ func build(cmd *cobra.Command, args []string) error { return err } opts.IIDFile = iidFile + iidFileRaw, err := cmd.Flags().GetString("iidfile-raw") + if err != nil { + return err + } + opts.IIDFileRaw = iidFileRaw // only set tls-verify if it has been changed by the user // if it hasn't we will read the registries.conf on the farm // nodes for further configuration diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index bae13da28a..c4f945cf64 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -118,11 +118,12 @@ func build(cmd *cobra.Command, args []string) error { } if cmd.Flag("iidfile").Changed { - f, err := os.Create(buildOpts.Iidfile) - if err != nil { + if err := os.WriteFile(buildOpts.Iidfile, []byte("sha256:"+report.ID), 0o644); err != nil { return err } - if _, err := f.WriteString("sha256:" + report.ID); err != nil { + } + if cmd.Flag("iidfile-raw").Changed { + if err := os.WriteFile(buildOpts.IidfileRaw, []byte(report.ID), 0o644); err != nil { return err } } diff --git a/docs/source/markdown/options/iidfile-raw.md b/docs/source/markdown/options/iidfile-raw.md new file mode 100644 index 0000000000..39d15d4b63 --- /dev/null +++ b/docs/source/markdown/options/iidfile-raw.md @@ -0,0 +1,7 @@ +####> This option file is used in: +####> podman build, farm build +####> If file is edited, make sure the changes +####> are applicable to all of those. +#### **--iidfile-raw**=*ImageIDfile* + +Write the built image's ID to the file without the algorithm prefix (e.g., `sha256:`). When `--platform` is specified more than once, attempting to use this option triggers an error. diff --git a/docs/source/markdown/options/metadata-file.md b/docs/source/markdown/options/metadata-file.md new file mode 100644 index 0000000000..f6326f5dfe --- /dev/null +++ b/docs/source/markdown/options/metadata-file.md @@ -0,0 +1,9 @@ +####> This option file is used in: +####> podman build +####> If file is edited, make sure the changes +####> are applicable to all of those. +#### **--metadata-file**=*MetadataFile* + +Write information about the built image to the named file. When `--platform` is specified more than once, attempting to use this option triggers an error. + +Note: This option is not supported in remote mode. diff --git a/docs/source/markdown/podman-build.1.md.in b/docs/source/markdown/podman-build.1.md.in index 53d603ea5c..6542db77cc 100644 --- a/docs/source/markdown/podman-build.1.md.in +++ b/docs/source/markdown/podman-build.1.md.in @@ -223,6 +223,8 @@ It does not affect _/etc/resolv.conf_ in the final image. @@option iidfile +@@option iidfile-raw + @@option inherit-annotations @@option inherit-labels @@ -255,6 +257,8 @@ This option is not supported on the remote client, including Mac and Windows @@option memory-swap +@@option metadata-file + @@option network.image @@option no-cache diff --git a/docs/source/markdown/podman-farm-build.1.md.in b/docs/source/markdown/podman-farm-build.1.md.in index 61f634f904..0364fdf96f 100644 --- a/docs/source/markdown/podman-farm-build.1.md.in +++ b/docs/source/markdown/podman-farm-build.1.md.in @@ -125,6 +125,8 @@ This option specifies the name of the farm to be used in the build process. @@option iidfile +@@option iidfile-raw + @@option inherit-annotations @@option inherit-labels diff --git a/go.mod b/go.mod index 016e284f93..cdffa9cd0c 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/checkpoint-restore/checkpointctl v1.5.0 github.com/checkpoint-restore/go-criu/v7 v7.2.0 github.com/containernetworking/plugins v1.9.0 - github.com/containers/buildah v1.42.0 + github.com/containers/buildah v1.42.1-0.20260126144005-964d45f717ce github.com/containers/gvisor-tap-vsock v0.8.7 github.com/containers/libhvee v0.10.1-0.20250829163521-178d10e67860 github.com/containers/ocicrypt v1.2.1 @@ -64,8 +64,8 @@ require ( github.com/vbauerster/mpb/v8 v8.11.3 github.com/vishvananda/netlink v1.3.1 go.podman.io/common v0.66.2-0.20260126213724-1e46b0756b39 - go.podman.io/image/v5 v5.38.1-0.20251209230740-724707234895 - go.podman.io/storage v1.61.1-0.20251209230740-724707234895 + go.podman.io/image/v5 v5.38.1-0.20260123202709-b5801a635dfa + go.podman.io/storage v1.61.1-0.20260123202709-b5801a635dfa golang.org/x/crypto v0.47.0 golang.org/x/net v0.49.0 golang.org/x/sync v0.19.0 @@ -94,13 +94,13 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/platforms v1.0.0-rc.2 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/containernetworking/cni v1.3.0 // indirect github.com/containers/common v0.64.2 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect - github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957 // indirect + github.com/containers/luksy v0.0.0-20251208191447-ca096313c38f // indirect github.com/coreos/go-oidc/v3 v3.16.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect @@ -112,7 +112,7 @@ require ( github.com/ebitengine/purego v0.9.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/fsouza/go-dockerclient v1.12.2 // indirect + github.com/fsouza/go-dockerclient v1.12.3 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -137,7 +137,7 @@ require ( github.com/mdlayher/socket v0.5.1 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mistifyio/go-zfs/v4 v4.0.0 // indirect - github.com/moby/buildkit v0.25.1 // indirect + github.com/moby/buildkit v0.26.3 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/moby/api v1.52.0 // indirect github.com/moby/moby/client v0.2.1 // indirect diff --git a/go.sum b/go.sum index 5b4af45c48..5a6f880756 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpY github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/checkpoint-restore/checkpointctl v1.5.0 h1:Uu+D2cOf/GUyCMk23Y8L69P6YoATTe6pH+Au64O3y28= github.com/checkpoint-restore/checkpointctl v1.5.0/go.mod h1:y5HRs1ZWQUZGyEuthlTHmTJN9PUMOjlaH6JvVaNq9kE= github.com/checkpoint-restore/go-criu/v7 v7.2.0 h1:qGiWA4App1gGlEfIJ68WR9jbezV9J7yZdjzglezcqKo= @@ -47,8 +47,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= -github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= +github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= @@ -57,8 +57,8 @@ github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEm github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.9.0 h1:Mg3SXBdRGkdXyFC4lcwr6u2ZB2SDeL6LC3U+QrEANuQ= github.com/containernetworking/plugins v1.9.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c= -github.com/containers/buildah v1.42.0 h1:hS+/sq6g74wNNYvX6d5/jx4awkwqibBxUxJ/P2oOETk= -github.com/containers/buildah v1.42.0/go.mod h1:azIYkIUVSEiVVQi4hPm9ZsxuVNqb8HdSMlvaBzr8MtU= +github.com/containers/buildah v1.42.1-0.20260126144005-964d45f717ce h1:JNPN3qlLtAZRzggCPK1ffZ4MWuDogG+NtIvQv2bINOY= +github.com/containers/buildah v1.42.1-0.20260126144005-964d45f717ce/go.mod h1:F10eTynOMnjfEzsX8pmZRIJEb5/3+mTEzmRHXB/6+hk= github.com/containers/common v0.64.2 h1:1xepE7QwQggUXxmyQ1Dbh6Cn0yd7ktk14sN3McSWf5I= github.com/containers/common v0.64.2/go.mod h1:o29GfYy4tefUuShm8mOn2AiL5Mpzdio+viHI7n24KJ4= github.com/containers/gvisor-tap-vsock v0.8.7 h1:mFMMU5CIXO9sbtsgECc90loUHx15km3AN6Zuhg3X4qM= @@ -67,8 +67,8 @@ github.com/containers/libhvee v0.10.1-0.20250829163521-178d10e67860 h1:YOhl3KCie github.com/containers/libhvee v0.10.1-0.20250829163521-178d10e67860/go.mod h1:/A6jL8HXzYB4aUQEjlyYImaQTgSw2jYZunSVCwqgaCI= 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/luksy v0.0.0-20250910190358-2cf5bc928957 h1:WxixhZ0typ8o668V0V7RVCZb3lNs58UF42RbwlQ4vdE= -github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957/go.mod h1:fGPsLPRi1etbHfe5o6sdx6ajsW810tI43uyF6ugmP/o= +github.com/containers/luksy v0.0.0-20251208191447-ca096313c38f h1:DWGVgZ9ToKBOMiBMv//ilrKPKEy4tQG752XtTJXYUQ0= +github.com/containers/luksy v0.0.0-20251208191447-ca096313c38f/go.mod h1:bynzkJ2rWsqgt0s31fuAdkHZBB3zrPtD6pV2zHt/qRk= github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= github.com/containers/psgo v1.10.0 h1:r9cEzAMVRtC0sw4ayIPjbd9EgF9pPaTCqKgDHhS0D/8= @@ -127,8 +127,8 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fsouza/go-dockerclient v1.12.2 h1:+pbP/SacoHfqaVZuiudvcdYGd9jzU7y9EcgoBOHivEI= -github.com/fsouza/go-dockerclient v1.12.2/go.mod h1:ZGCkAsnBGjnTRG9wV6QaICPJ5ig2KlaxTccDQy5WQ38= +github.com/fsouza/go-dockerclient v1.12.3 h1:CEsX4/msyMEekHAR9Pf8XniZBtwGo0Kl+mLPQ/AnSys= +github.com/fsouza/go-dockerclient v1.12.3/go.mod h1:gl0t2KUfrsLbm4tw5/ySsJkkFpi7Fz9gXzY2BKLEvZA= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -255,8 +255,8 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs/v4 v4.0.0 h1:sU0+5dX45tdDK5xNZ3HBi95nxUc48FS92qbIZEvpAg4= github.com/mistifyio/go-zfs/v4 v4.0.0/go.mod h1:weotFtXTHvBwhr9Mv96KYnDkTPBOHFUbm9cBmQpesL0= -github.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8= -github.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc= +github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g= +github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= @@ -433,8 +433,8 @@ go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= @@ -447,10 +447,10 @@ go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOV go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.podman.io/common v0.66.2-0.20260126213724-1e46b0756b39 h1:qMQW6s4s6dmewSyyR5bWWvdGXt9SnRSdXTHiOPvbgcI= go.podman.io/common v0.66.2-0.20260126213724-1e46b0756b39/go.mod h1:bGauG8nGM+EIHwcCXqhHFCOpdrMZrlap4yrlmNUJs1Y= -go.podman.io/image/v5 v5.38.1-0.20251209230740-724707234895 h1:WWyrZFLYIg/BCCB5nVQJwPCbT8SbrmmhY6rUfM1EWzM= -go.podman.io/image/v5 v5.38.1-0.20251209230740-724707234895/go.mod h1:aplZfUFBE3o4Ttj+hWH3QRmBRjPHOuaEnC9/yaMgxL0= -go.podman.io/storage v1.61.1-0.20251209230740-724707234895 h1:v9uNYnaG3E6VArt6+6SZLzE1rvG6/7baZvE8jmijM6o= -go.podman.io/storage v1.61.1-0.20251209230740-724707234895/go.mod h1:Z4Wvuj8fy+zTePj4wzBveklhs2CnlfcBgrUMW+bFvos= +go.podman.io/image/v5 v5.38.1-0.20260123202709-b5801a635dfa h1:T/8IVrvcT8N7+1P13Tr1HbAwfa8KQMIMxf2FF9axK8k= +go.podman.io/image/v5 v5.38.1-0.20260123202709-b5801a635dfa/go.mod h1:8wlUZxYaYCtP1IG7eqoy9a4sNULafIV9o4Cj5Jr9Eaw= +go.podman.io/storage v1.61.1-0.20260123202709-b5801a635dfa h1:u/1HgvlgbgV3xngyb8iCSwK55kYI4HwjPVfXCPORJ+s= +go.podman.io/storage v1.61.1-0.20260123202709-b5801a635dfa/go.mod h1:yuLB1ikwsdGrGqSGBWv7fMbOeHupCaMn5iJ1biqxrpI= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -465,8 +465,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= +golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/pkg/farm/farm.go b/pkg/farm/farm.go index 7eee696137..665bdd6a56 100644 --- a/pkg/farm/farm.go +++ b/pkg/farm/farm.go @@ -268,6 +268,7 @@ func (f *Farm) Build(ctx context.Context, schedule Schedule, options entities.Bu listBuilderOptions := listBuilderOptions{ cleanup: options.Cleanup, iidFile: options.IIDFile, + iidFileRaw: options.IIDFileRaw, authfile: options.Authfile, skipTLSVerify: options.SkipTLSVerify, } diff --git a/pkg/farm/list_builder.go b/pkg/farm/list_builder.go index 03423138a1..09379c97ad 100644 --- a/pkg/farm/list_builder.go +++ b/pkg/farm/list_builder.go @@ -16,6 +16,7 @@ import ( type listBuilderOptions struct { cleanup bool iidFile string + iidFileRaw string authfile string skipTLSVerify *bool } @@ -130,6 +131,11 @@ func (l *listLocal) build(ctx context.Context, images map[entities.BuildReport]e return "", err } } + if l.options.iidFileRaw != "" { + if err := os.WriteFile(l.options.iidFileRaw, []byte(listID), 0o644); err != nil { + return "", err + } + } return l.listName, nil } diff --git a/test/buildah-bud/apply-podman-deltas b/test/buildah-bud/apply-podman-deltas index 5773c22c9a..3c6513b4f0 100755 --- a/test/buildah-bud/apply-podman-deltas +++ b/test/buildah-bud/apply-podman-deltas @@ -279,6 +279,9 @@ skip_if_remote "compat API does not support oci-archive tags" \ skip_if_remote "http_proxy env is not sent via remote API" \ "build proxy - ADD URL" +skip_if_remote "--metadata-file not supported in remote mode" \ + "bud cache by format" + ############################################################################### # BEGIN tests which are skipped due to actual podman or podman-remote bugs. @@ -329,6 +332,12 @@ skip "FIXME: 2024-05-28 new VMs from #338" \ # 2025-04-01 FIXME wrong exit code from git related failures from #25756 skip_if_remote "FIXME: 2025-04-01 git related errors returning wrong exit code" \ "bud with ADD with git repository source" +# +# 2026-02-02 buildah's overlay-over-context-directory fails with process substitution +# FIXME: Don't use process substitution for Containerfile in buildah tests +skip "process substitution with overlay context not supported" \ + "build-with-timestamp-applies-to-oci-archive" \ + "build-with-timestamp-applies-to-oci-archive-with-base" # END temporary workarounds that must be reevaluated periodically ############################################################################### diff --git a/test/buildah-bud/buildah-tests.diff b/test/buildah-bud/buildah-tests.diff index 6611221564..e746000b48 100644 --- a/test/buildah-bud/buildah-tests.diff +++ b/test/buildah-bud/buildah-tests.diff @@ -1,16 +1,17 @@ -From 4c30f5e698bc1d4ca498347435174a6be7232876 Mon Sep 17 00:00:00 2001 +From 1ca19690f26005148482b1fbeff0aa52a67ee898 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Thu, 6 Oct 2022 17:32:59 -0600 Subject: [PATCH] tweaks for running buildah tests under podman Signed-off-by: Ed Santiago Signed-off-by: Paul Holzinger +Signed-off-by: Danish Prakash --- tests/helpers.bash | 166 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 4 deletions(-) diff --git a/tests/helpers.bash b/tests/helpers.bash -index 5acd0a3c3..7a0721305 100644 +index d08221a52759..5e9c818092cb 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash @@ -85,6 +85,42 @@ EOF @@ -55,8 +56,8 @@ index 5acd0a3c3..7a0721305 100644 + fi } - function starthttpd() { # directory [working-directory-or-"" [certfile, keyfile]] -@@ -149,6 +185,22 @@ function teardown_tests() { + function starthttpd() { # directoryspecs [working-directory-or-"" [certfile, keyfile]] +@@ -154,6 +190,22 @@ function teardown_tests() { stop_git_daemon stop_registry @@ -79,7 +80,7 @@ index 5acd0a3c3..7a0721305 100644 # Workaround for #1991 - buildah + overlayfs leaks mount points. # Many tests leave behind /var/tmp/.../root/overlay and sub-mounts; # let's find those and clean them up, otherwise 'rm -rf' fails. -@@ -270,7 +322,12 @@ function copy() { +@@ -275,7 +327,12 @@ function copy() { } function podman() { @@ -93,7 +94,7 @@ index 5acd0a3c3..7a0721305 100644 } # There are various scenarios where we would like to execute `tests` as rootless user, however certain commands like `buildah mount` -@@ -377,8 +434,86 @@ function run_buildah() { +@@ -382,8 +439,86 @@ function run_buildah() { --retry) retry=3; shift;; # retry network flakes esac @@ -181,7 +182,7 @@ index 5acd0a3c3..7a0721305 100644 # If session is rootless and `buildah mount` is invoked, perform unshare, # since normal user cannot mount a filesystem unless they're in a user namespace along with its own mount namespace. -@@ -392,8 +527,8 @@ function run_buildah() { +@@ -397,8 +532,8 @@ function run_buildah() { retry=$(( retry - 1 )) # stdout is only emitted upon error; this echo is to help a debugger @@ -192,7 +193,7 @@ index 5acd0a3c3..7a0721305 100644 # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" -@@ -420,6 +555,9 @@ function run_buildah() { +@@ -425,6 +560,9 @@ function run_buildah() { false fi @@ -202,7 +203,7 @@ index 5acd0a3c3..7a0721305 100644 if [ -n "$expected_rc" ]; then if [ "$status" -eq "$expected_rc" ]; then return -@@ -757,6 +895,26 @@ function skip_if_no_unshare() { +@@ -753,6 +891,26 @@ function skip_if_no_unshare() { fi } @@ -230,5 +231,5 @@ index 5acd0a3c3..7a0721305 100644 # start_git_daemon # ###################### -- -2.51.0 +2.51.1 diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index d0208b2902..bf2357e912 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -310,6 +310,21 @@ var _ = Describe("Podman build", func() { Expect("sha256:" + data[0].ID).To(Equal(string(id))) }) + It("podman build basic alpine and print id to external file without prefix (--iidfile-raw)", func() { + targetFile := filepath.Join(podmanTest.TempDir, "idFileRaw") + + session := podmanTest.Podman([]string{"build", "--pull-never", "build/basicalpine", "--iidfile-raw", targetFile}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + id, _ := os.ReadFile(targetFile) + + // Verify that id is correct (no sha256: prefix) + inspect := podmanTest.Podman([]string{"inspect", string(id)}) + inspect.WaitWithDefaultTimeout() + data := inspect.InspectImageJSON() + Expect(data[0].ID).To(Equal(string(id))) + }) + It("podman Test PATH and reserved annotation in built image", func() { path := "/tmp:/bin:/usr/bin:/usr/sbin" session := podmanTest.Podman([]string{ @@ -1385,4 +1400,20 @@ COPY --from=img2 /etc/alpine-release /prefix-test/container-prefix.txt` session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) }) + + It("podman build basic alpine and write metadata to external file", func() { + SkipIfRemote("--metadata-file is not supported in remote mode") + targetFile := filepath.Join(podmanTest.TempDir, "metadata.json") + + session := podmanTest.Podman([]string{"build", "--pull-never", "build/basicalpine", "--metadata-file", targetFile}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + + // Verify that metadata file exists and contains expected keys + metadata, err := os.ReadFile(targetFile) + Expect(err).ToNot(HaveOccurred()) + Expect(metadata).To(ContainSubstring("containerimage.config.digest")) + Expect(metadata).To(ContainSubstring("containerimage.digest")) + Expect(metadata).To(ContainSubstring("containerimage.descriptor")) + }) }) diff --git a/vendor/github.com/containerd/platforms/defaults_windows.go b/vendor/github.com/containerd/platforms/defaults_windows.go index 0165adea7e..64e2846674 100644 --- a/vendor/github.com/containerd/platforms/defaults_windows.go +++ b/vendor/github.com/containerd/platforms/defaults_windows.go @@ -38,5 +38,5 @@ func DefaultSpec() specs.Platform { // Default returns the current platform's default platform specification. func Default() MatchComparer { - return Only(DefaultSpec()) + return &windowsMatchComparer{Matcher: NewMatcher(DefaultSpec())} } diff --git a/vendor/github.com/containerd/platforms/platform_windows_compat.go b/vendor/github.com/containerd/platforms/platform_windows_compat.go index 7f3d9966bc..f31ebe0c9e 100644 --- a/vendor/github.com/containerd/platforms/platform_windows_compat.go +++ b/vendor/github.com/containerd/platforms/platform_windows_compat.go @@ -42,18 +42,30 @@ const ( // rs5 (version 1809, codename "Redstone 5") corresponds to Windows Server // 2019 (ltsc2019), and Windows 10 (October 2018 Update). rs5 = 17763 + // ltsc2019 (Windows Server 2019) is an alias for [RS5]. + ltsc2019 = rs5 // v21H2Server corresponds to Windows Server 2022 (ltsc2022). v21H2Server = 20348 + // ltsc2022 (Windows Server 2022) is an alias for [v21H2Server] + ltsc2022 = v21H2Server // v22H2Win11 corresponds to Windows 11 (2022 Update). v22H2Win11 = 22621 + + // v23H2 is the 23H2 release in the Windows Server annual channel. + v23H2 = 25398 + + // Windows Server 2025 build 26100 + v25H1Server = 26100 + ltsc2025 = v25H1Server ) // List of stable ABI compliant ltsc releases // Note: List must be sorted in ascending order var compatLTSCReleases = []uint16{ - v21H2Server, + ltsc2022, + ltsc2025, } // CheckHostAndContainerCompat checks if given host and container @@ -70,18 +82,27 @@ func checkWindowsHostAndContainerCompat(host, ctr windowsOSVersion) bool { } // If host is < WS 2022, exact version match is required - if host.Build < v21H2Server { + if host.Build < ltsc2022 { return host.Build == ctr.Build } - var supportedLtscRelease uint16 + // Find the latest LTSC version that is earlier than the host version. + // This is the earliest version of container that the host can run. + // + // If the host version is an LTSC, then it supports compatibility with + // everything from the previous LTSC up to itself, so we want supportedLTSCRelease + // to be the previous entry. + // + // If no match is found, then we know that the host is LTSC2022 exactly, + // since we already checked that it's not less than LTSC2022. + var supportedLTSCRelease uint16 = ltsc2022 for i := len(compatLTSCReleases) - 1; i >= 0; i-- { - if host.Build >= compatLTSCReleases[i] { - supportedLtscRelease = compatLTSCReleases[i] + if host.Build > compatLTSCReleases[i] { + supportedLTSCRelease = compatLTSCReleases[i] break } } - return ctr.Build >= supportedLtscRelease && ctr.Build <= host.Build + return supportedLTSCRelease <= ctr.Build && ctr.Build <= host.Build } func getWindowsOSVersion(osVersionPrefix string) windowsOSVersion { @@ -114,18 +135,6 @@ func getWindowsOSVersion(osVersionPrefix string) windowsOSVersion { } } -func winRevision(v string) int { - parts := strings.Split(v, ".") - if len(parts) < 4 { - return 0 - } - r, err := strconv.Atoi(parts[3]) - if err != nil { - return 0 - } - return r -} - type windowsVersionMatcher struct { windowsOSVersion } @@ -149,8 +158,7 @@ type windowsMatchComparer struct { func (c *windowsMatchComparer) Less(p1, p2 specs.Platform) bool { m1, m2 := c.Match(p1), c.Match(p2) if m1 && m2 { - r1, r2 := winRevision(p1.OSVersion), winRevision(p2.OSVersion) - return r1 > r2 + return p1.OSVersion > p2.OSVersion } return m1 && !m2 } diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index 66b950241e..2c71589ffb 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -30,13 +30,13 @@ env: #### # GCE project where images live IMAGE_PROJECT: "libpod-218412" - FEDORA_NAME: "fedora-42" - PRIOR_FEDORA_NAME: "fedora-41" + FEDORA_NAME: "fedora-43" + PRIOR_FEDORA_NAME: "fedora-42" RAWHIDE_NAME: "rawhide" - DEBIAN_NAME: "debian-13" + DEBIAN_NAME: "debian-14" # Image identifiers - IMAGE_SUFFIX: "c20250910t092246z-f42f41d13" + IMAGE_SUFFIX: "c20251211t152018z-f43f42d14" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" RAWHIDE_CACHE_IMAGE_NAME: "rawhide-${IMAGE_SUFFIX}" # Used temporarily for rust-podman-sequoia. After that RPM is available in stable Fedora releases, we can stop testing against Rawhide again. @@ -127,7 +127,7 @@ vendor_task: # Runs within Cirrus's "community cluster" container: - image: docker.io/library/golang:1.24.2 + image: docker.io/library/golang:1.24.6 cpu: 1 memory: 1 @@ -224,6 +224,18 @@ integration_task: DISTRO_NV: "${DEBIAN_NAME}" IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'vfs' + - env: + DISTRO_NV: "${FEDORA_NAME}" + IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'vfs' + BUILDAH_RUNTIME: runc + RUNTIME_N: " using runc" + - env: + DISTRO_NV: "${PRIOR_FEDORA_NAME}" + IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'vfs' + BUILDAH_RUNTIME: runc + RUNTIME_N: " using runc" # OVERLAY - env: DISTRO_NV: "${FEDORA_NAME}" @@ -246,6 +258,18 @@ integration_task: IMAGE_NAME: "${RAWHIDE_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' TEST_BUILD_TAGS: 'containers_image_sequoia' + - env: + DISTRO_NV: "${FEDORA_NAME}" + IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'overlay' + BUILDAH_RUNTIME: runc + RUNTIME_N: " using runc" + - env: + DISTRO_NV: "${PRIOR_FEDORA_NAME}" + IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + STORAGE_DRIVER: 'overlay' + BUILDAH_RUNTIME: runc + RUNTIME_N: " using runc" gce_instance: &integration_gce_instance image_name: "$IMAGE_NAME" @@ -270,53 +294,6 @@ integration_task: package_versions_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh packages' golang_version_script: '$GOSRC/$SCRIPT_BASE/logcollector.sh golang' -non_blocking_integration_task: - name: "Integration $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER (non-blocking)" - alias: non_blocking_integration - skip: *not_build_docs - depends_on: *smoke_vendor - - matrix: - # VFS - - env: - DISTRO_NV: "${FEDORA_NAME}" - IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'vfs' - BUILDAH_RUNTIME: runc - RUNTIME_N: " using runc" - - env: - DISTRO_NV: "${PRIOR_FEDORA_NAME}" - IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'vfs' - BUILDAH_RUNTIME: runc - RUNTIME_N: " using runc" - # OVERLAY - - env: - DISTRO_NV: "${FEDORA_NAME}" - IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'overlay' - BUILDAH_RUNTIME: runc - RUNTIME_N: " using runc" - - env: - DISTRO_NV: "${PRIOR_FEDORA_NAME}" - IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'overlay' - BUILDAH_RUNTIME: runc - RUNTIME_N: " using runc" - - gce_instance: - <<: *integration_gce_instance - - # Separate scripts for separate outputs, makes debugging easier. - setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' - build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}' - integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}' - - binary_artifacts: - path: ./bin/* - - always: *standardlogs - integration_rootless_task: name: "Integration rootless $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER" alias: integration_rootless @@ -345,28 +322,6 @@ integration_rootless_task: IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' PRIV_NAME: rootless - - gce_instance: - <<: *integration_gce_instance - - # Separate scripts for separate outputs, makes debugging easier. - setup_script: '${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' - build_script: '${SCRIPT_BASE}/build.sh |& ${_TIMESTAMP}' - integration_test_script: '${SCRIPT_BASE}/test.sh integration |& ${_TIMESTAMP}' - - binary_artifacts: - path: ./bin/* - - always: - <<: *standardlogs - -non_blocking_integration_rootless_task: - name: "Integration rootless $DISTRO_NV$RUNTIME_N w/ $STORAGE_DRIVER (non-blocking)" - alias: non_blocking_integration_rootless - skip: *not_build_docs - depends_on: *smoke_vendor - - matrix: - env: DISTRO_NV: "${FEDORA_NAME}" IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index 42a25d0170..69ad7daccf 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -54,7 +54,7 @@ ifeq ($(BUILDDEBUG), 1) endif # Managed by renovate. -export GOLANGCI_LINT_VERSION := 2.1.0 +export GOLANGCI_LINT_VERSION := 2.8.0 # make all BUILDDEBUG=1 # Note: Uses the -N -l go compiler options to disable compiler optimizations @@ -66,16 +66,31 @@ bin/buildah: $(SOURCES) internal/mkcw/embed/entrypoint_amd64.gz $(GO_BUILD) $(BUILDAH_LDFLAGS) $(GO_GCFLAGS) "$(GOGCFLAGS)" -o $@ $(BUILDFLAGS) ./cmd/buildah test -z "${SELINUXOPT}" || chcon --verbose -t $(SELINUXTYPE) $@ -ifneq ($(shell $(AS) --version | grep x86_64),) internal/mkcw/embed/entrypoint_amd64.gz: internal/mkcw/embed/entrypoint_amd64 gzip -k9nf $^ +internal/mkcw/embed/entrypoint_arm64.gz: internal/mkcw/embed/entrypoint_arm64 + gzip -k9nf $^ +internal/mkcw/embed/entrypoint_ppc64le.gz: internal/mkcw/embed/entrypoint_ppc64le + gzip -k9nf $^ +internal/mkcw/embed/entrypoint_s390x.gz: internal/mkcw/embed/entrypoint_s390x + gzip -k9nf $^ -internal/mkcw/embed/entrypoint_amd64: internal/mkcw/embed/entrypoint_amd64.s +ifneq ($(shell $(AS) --version | grep -E 'x86_64-([^-]+-)?linux'),) +internal/mkcw/embed/entrypoint_amd64: internal/mkcw/embed/asm/entrypoint_amd64.s $(AS) -o $(patsubst %.s,%.o,$^) $^ $(LD) -o $@ $(patsubst %.s,%.o,$^) $(STRIP) $@ +else +internal/mkcw/embed/entrypoint_amd64: internal/mkcw/embed/entrypoint_amd64.s internal/mkcw/embed/entrypoint.go + GOOS=linux GOARCH=amd64 $(GO) build -ldflags "-E _start -s" -o $@ ./internal/mkcw/embed endif +internal/mkcw/embed/entrypoint_arm64: internal/mkcw/embed/entrypoint_arm64.s internal/mkcw/embed/entrypoint.go + GOOS=linux GOARCH=arm64 $(GO) build -ldflags "-E _start -s" -o $@ ./internal/mkcw/embed +internal/mkcw/embed/entrypoint_ppc64le: internal/mkcw/embed/entrypoint_ppc64le.s internal/mkcw/embed/entrypoint.go + GOOS=linux GOARCH=ppc64le $(GO) build -ldflags "-E _start -s" -o $@ ./internal/mkcw/embed +internal/mkcw/embed/entrypoint_s390x: internal/mkcw/embed/entrypoint_s390x.s internal/mkcw/embed/entrypoint.go + GOOS=linux GOARCH=s390x $(GO) build -ldflags "-E _start -s" -o $@ ./internal/mkcw/embed .PHONY: buildah buildah: bin/buildah @@ -88,7 +103,7 @@ FREEBSD_CROSS_TARGETS := $(filter bin/buildah.freebsd.%,$(ALL_CROSS_TARGETS)) .PHONY: cross cross: $(LINUX_CROSS_TARGETS) $(DARWIN_CROSS_TARGETS) $(WINDOWS_CROSS_TARGETS) $(FREEBSD_CROSS_TARGETS) -bin/buildah.%: $(SOURCES) +bin/buildah.%: $(SOURCES) internal/mkcw/embed/entrypoint_amd64.gz mkdir -p ./bin GOOS=$(word 2,$(subst ., ,$@)) GOARCH=$(word 3,$(subst ., ,$@)) $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ -tags "containers_image_openpgp" ./cmd/buildah @@ -118,7 +133,7 @@ bin/passwd: tests/passwd/passwd.go .PHONY: clean clean: - $(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true + $(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true internal/mkcw/embed/entrypoint_amd64 internal/mkcw/embed/entrypoint_arm64 internal/mkcw/embed/entrypoint_ppc64le internal/mkcw/embed/entrypoint_s390x internal/mkcw/embed/*.gz internal/mkcw/embed/asm/*.o $(MAKE) -C docs clean .PHONY: docs @@ -197,6 +212,10 @@ lint: install.tools ./tests/tools/build/golangci-lint run $(LINTFLAGS) ./tests/tools/build/golangci-lint run --tests=false $(LINTFLAGS) +.PHONY: fmt +fmt: install.tools + ./tests/tools/build/golangci-lint fmt $(LINTFLAGS) + # CAUTION: This is not a replacement for RPMs provided by your distro. # Only intended to build and test the latest unreleased changes. .PHONY: rpm diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go index 1ae6c01286..642e57fb56 100644 --- a/vendor/github.com/containers/buildah/commit.go +++ b/vendor/github.com/containers/buildah/commit.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/containers/buildah/internal/metadata" "github.com/containers/buildah/pkg/blobcache" "github.com/containers/buildah/util" encconfig "github.com/containers/ocicrypt/config" @@ -76,7 +77,8 @@ type CommitOptions struct { // github.com/containers/image/types SystemContext to hold credentials // and other authentication/authorization information. SystemContext *types.SystemContext - // IIDFile tells the builder to write the image ID to the specified file + // IIDFile tells the builder to write the image's ID, preceded by + // "sha256:", to the specified file. IIDFile string // Squash tells the builder to produce an image with a single layer // instead of with possibly more than one layer. @@ -304,11 +306,36 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe return imageID, err } +// CommitResults is a structure returned when CommitResults() succeeds. +type CommitResults struct { + ImageID string // a local image ID, or part of the digest of the image's config blob + Canonical reference.Canonical // set if destination included a DockerReference + MediaType string // image manifest MIME type, always returned + ImageManifest []byte // raw image manifest, always returned + Digest digest.Digest // digest of the manifest, always returned + Metadata map[string]any // always returned, format is flexible +} + // Commit writes the contents of the container, along with its updated // configuration, to a new image in the specified location, and if we know how, // add any additional tags that were specified. Returns the ID of the new image -// if commit was successful and the image destination was local. +// if commit was successful and the image destination was local, a canonical +// reference if the destination ImageReference include a DockerReference, and +// the digest of the written image's manifest. +// Commit() is implemented as a wrapper around CommitResults(). func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) (string, reference.Canonical, digest.Digest, error) { + results, err := b.CommitResults(ctx, dest, options) + if err != nil { + return "", nil, "", err + } + return results.ImageID, results.Canonical, results.Digest, nil +} + +// CommitResults writes the contents of the container, along with its updated +// configuration, to a new image in the specified location, and if we know how, +// add any additional tags that were specified. Returns a CommitResults +// structure. +func (b *Builder) CommitResults(ctx context.Context, dest types.ImageReference, options CommitOptions) (*CommitResults, error) { var ( imgID string src types.ImageReference @@ -325,7 +352,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options // work twice. if options.OmitTimestamp { if options.HistoryTimestamp != nil { - return imgID, nil, "", fmt.Errorf("OmitTimestamp and HistoryTimestamp can not be used together") + return nil, fmt.Errorf("OmitTimestamp and HistoryTimestamp can not be used together") } timestamp := time.Unix(0, 0).UTC() options.HistoryTimestamp = ×tamp @@ -339,7 +366,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options nameToRemove = stringid.GenerateRandomID() + "-tmp" dest2, err := is.Transport.ParseStoreReference(b.store, nameToRemove) if err != nil { - return imgID, nil, "", fmt.Errorf("creating temporary destination reference for image: %w", err) + return nil, fmt.Errorf("creating temporary destination reference for image: %w", err) } dest = dest2 } @@ -348,23 +375,23 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options blocked, err := isReferenceBlocked(dest, systemContext) if err != nil { - return "", nil, "", fmt.Errorf("checking if committing to registry for %q is blocked: %w", transports.ImageName(dest), err) + return nil, fmt.Errorf("checking if committing to registry for %q is blocked: %w", transports.ImageName(dest), err) } if blocked { - return "", nil, "", fmt.Errorf("commit access to registry for %q is blocked by configuration", transports.ImageName(dest)) + return nil, fmt.Errorf("commit access to registry for %q is blocked by configuration", transports.ImageName(dest)) } // Load the system signing policy. commitPolicy, err := signature.DefaultPolicy(systemContext) if err != nil { - return "", nil, "", fmt.Errorf("obtaining default signature policy: %w", err) + return nil, fmt.Errorf("obtaining default signature policy: %w", err) } // Override the settings for local storage to make sure that we can always read the source "image". commitPolicy.Transports[is.Transport.Name()] = storageAllowedPolicyScopes policyContext, err := signature.NewPolicyContext(commitPolicy) if err != nil { - return imgID, nil, "", fmt.Errorf("creating new signature policy context: %w", err) + return nil, fmt.Errorf("creating new signature policy context: %w", err) } defer func() { if err2 := policyContext.Destroy(); err2 != nil { @@ -375,11 +402,11 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options // Check if the commit is blocked by $BUILDER_REGISTRY_SOURCES. insecure, err := checkRegistrySourcesAllows("commit to", dest) if err != nil { - return imgID, nil, "", err + return nil, err } if insecure { if systemContext.DockerInsecureSkipTLSVerify == types.OptionalBoolFalse { - return imgID, nil, "", fmt.Errorf("can't require tls verification on an insecured registry") + return nil, fmt.Errorf("can't require tls verification on an insecured registry") } systemContext.DockerInsecureSkipTLSVerify = types.OptionalBoolTrue systemContext.OCIInsecureSkipTLSVerify = true @@ -393,7 +420,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options if len(options.SBOMScanOptions) != 0 { var scansDirectory string if extraImageContent, extraLocalContent, scansDirectory, err = b.sbomScan(ctx, options); err != nil { - return imgID, nil, "", fmt.Errorf("scanning rootfs to generate SBOM for container %q: %w", b.ContainerID, err) + return nil, fmt.Errorf("scanning rootfs to generate SBOM for container %q: %w", b.ContainerID, err) } if scansDirectory != "" { defer func() { @@ -418,7 +445,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options // Build an image reference from which we can copy the finished image. src, err = b.makeContainerImageRef(options) if err != nil { - return imgID, nil, "", fmt.Errorf("computing layer digests and building metadata for container %q: %w", b.ContainerID, err) + return nil, fmt.Errorf("computing layer digests and building metadata for container %q: %w", b.ContainerID, err) } // In case we're using caching, decide how to handle compression for a cache. // If we're using blob caching, set it up for the source. @@ -431,12 +458,12 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } cache, err := blobcache.NewBlobCache(src, options.BlobDirectory, compress) if err != nil { - return imgID, nil, "", fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(src), options.BlobDirectory, err) + return nil, fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(src), options.BlobDirectory, err) } maybeCachedSrc = cache cache, err = blobcache.NewBlobCache(dest, options.BlobDirectory, compress) if err != nil { - return imgID, nil, "", fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(dest), options.BlobDirectory, err) + return nil, fmt.Errorf("wrapping image reference %q in blob cache at %q: %w", transports.ImageName(dest), options.BlobDirectory, err) } maybeCachedDest = cache } @@ -457,7 +484,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options var manifestBytes []byte if manifestBytes, err = retryCopyImage(ctx, policyContext, maybeCachedDest, maybeCachedSrc, dest, getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "", false, options.SignBy, options.OciEncryptLayers, options.OciEncryptConfig, nil, destinationTimestamp), options.MaxRetries, options.RetryDelay); err != nil { - return imgID, nil, "", fmt.Errorf("copying layers and metadata for container %q: %w", b.ContainerID, err) + return nil, fmt.Errorf("copying layers and metadata for container %q: %w", b.ContainerID, err) } // If we've got more names to attach, and we know how to do that for // the transport that we're writing the new image to, add them now. @@ -466,10 +493,10 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options case is.Transport.Name(): _, img, err := is.ResolveReference(dest) if err != nil { - return imgID, nil, "", fmt.Errorf("locating just-written image %q: %w", transports.ImageName(dest), err) + return nil, fmt.Errorf("locating just-written image %q: %w", transports.ImageName(dest), err) } if err = util.AddImageNames(b.store, "", systemContext, img, options.AdditionalTags); err != nil { - return imgID, nil, "", fmt.Errorf("setting image names to %v: %w", append(img.Names, options.AdditionalTags...), err) + return nil, fmt.Errorf("setting image names to %v: %w", append(img.Names, options.AdditionalTags...), err) } logrus.Debugf("assigned names %v to image %q", img.Names, img.ID) default: @@ -480,7 +507,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options if dest.Transport().Name() == is.Transport.Name() { dest2, img, err := is.ResolveReference(dest) if err != nil { - return imgID, nil, "", fmt.Errorf("locating image %q in local storage: %w", transports.ImageName(dest), err) + return nil, fmt.Errorf("locating image %q in local storage: %w", transports.ImageName(dest), err) } dest = dest2 imgID = img.ID @@ -492,13 +519,13 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } if len(toPruneNames) > 0 { if err = b.store.RemoveNames(imgID, toPruneNames); err != nil { - return imgID, nil, "", fmt.Errorf("failed to remove temporary name from image %q: %w", imgID, err) + return nil, fmt.Errorf("failed to remove temporary name from image %q: %w", imgID, err) } logrus.Debugf("removing %v from assigned names to image %q", nameToRemove, img.ID) } if options.IIDFile != "" { if err = os.WriteFile(options.IIDFile, []byte("sha256:"+img.ID), 0o644); err != nil { - return imgID, nil, "", err + return nil, err } } } @@ -521,7 +548,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options return nil }() if err != nil { - return imgID, nil, "", err + return nil, err } } @@ -529,15 +556,15 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options // reference for the image. manifestDigest, err := manifest.Digest(manifestBytes) if err != nil { - return imgID, nil, "", fmt.Errorf("computing digest of manifest of new image %q: %w", transports.ImageName(dest), err) + return nil, fmt.Errorf("computing digest of manifest of new image %q: %w", transports.ImageName(dest), err) } - if imgID == "" { - parsedManifest, err := manifest.FromBlob(manifestBytes, manifest.GuessMIMEType(manifestBytes)) - if err != nil { - return imgID, nil, "", fmt.Errorf("parsing written manifest to determine the image's ID: %w", err) - } - configInfo := parsedManifest.ConfigInfo() - if configInfo.Size > 2 && configInfo.Digest.Validate() == nil { // don't be returning a digest of "" or "{}" + parsedManifest, err := manifest.FromBlob(manifestBytes, manifest.GuessMIMEType(manifestBytes)) + if err != nil { + return nil, fmt.Errorf("parsing written manifest to determine the image's ID: %w", err) + } + configInfo := parsedManifest.ConfigInfo() + if configInfo.Size > 2 && configInfo.Digest.Validate() == nil { // don't be returning a digest of "" or "{}" + if imgID == "" { imgID = configInfo.Digest.Encoded() } } @@ -553,9 +580,28 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options if options.Manifest != "" { manifestID, err := b.addManifest(ctx, options.Manifest, imgID) if err != nil { - return imgID, nil, "", err + return nil, err } logrus.Debugf("added imgID %s to manifestID %s", imgID, manifestID) } - return imgID, ref, manifestDigest, nil + + descriptor := v1.Descriptor{ + MediaType: manifest.GuessMIMEType(manifestBytes), + Digest: manifestDigest, + Size: int64(len(manifestBytes)), + } + metadata, err := metadata.Build(configInfo.Digest, descriptor) + if err != nil { + return nil, fmt.Errorf("building metadata map for image: %w", err) + } + + results := CommitResults{ + ImageID: imgID, + Canonical: ref, + MediaType: descriptor.MediaType, + ImageManifest: manifestBytes, + Digest: manifestDigest, + Metadata: metadata, + } + return &results, nil } diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index 9aa662cb68..3b430ba7fd 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -1168,7 +1168,7 @@ func copierHandlerStat(req request, pm *fileutils.PatternMatcher, idMappings *id hostPair := idtools.IDPair{UID: uid, GID: gid} uid, gid, err = idMappings.ToContainer(hostPair) if err != nil { - return errorResponse("copier: stat: mapping host filesystem owners %#v to container filesystem owners: %w", hostPair, err) + return errorResponse("copier: stat: mapping host filesystem owners %#v to container filesystem owners: %v", hostPair, err) } } result.UID, result.GID = int64(uid), int64(gid) @@ -2227,7 +2227,7 @@ func copierHandlerMkdir(req request, idMappings *idtools.IDMappings) (*response, return errorResponse("copier: mkdir: error setting owner of %q to %d:%d: %v", path, dirUID, dirGID, err) } if err = chmod(path, dirMode); err != nil { - return errorResponse("copier: mkdir: error setting permissions on %q to 0%o: %v", path, dirMode) + return errorResponse("copier: mkdir: error setting permissions on %q to 0%o: %v", path, dirMode, err) } created = append(created, path) } else { @@ -2409,7 +2409,7 @@ func copierHandlerEnsure(req request, idMappings *idtools.IDMappings) *response return errorResponse("copier: ensure: error setting owner of %q to %d:%d: %v", leaf, uid, gid, err) } if err = chmod(filepath.Join(req.Root, leaf), mode); err != nil { - return errorResponse("copier: ensure: error setting permissions on %q to 0%o: %v", leaf, mode) + return errorResponse("copier: ensure: error setting permissions on %q to 0%o: %v", leaf, mode, err) } if item.ModTime != nil { if err := os.Chtimes(filepath.Join(req.Root, leaf), *item.ModTime, *item.ModTime); err != nil { diff --git a/vendor/github.com/containers/buildah/define/build.go b/vendor/github.com/containers/buildah/define/build.go index 0b1d20800d..e07a01d7d0 100644 --- a/vendor/github.com/containers/buildah/define/build.go +++ b/vendor/github.com/containers/buildah/define/build.go @@ -262,6 +262,8 @@ type BuildOptions struct { DefaultMountsFilePath string // IIDFile tells the builder to write the image ID to the specified file IIDFile string + // IIDFileRaw tells the builder to write the image ID to the specified file without the algorithm prefix + IIDFileRaw string // Squash tells the builder to produce an image with a single layer instead of with // possibly more than one layer, by only committing a new layer after processing the // final instruction. @@ -418,4 +420,7 @@ type BuildOptions struct { // CreatedAnnotation controls whether or not an "org.opencontainers.image.created" // annotation is present in the output image. CreatedAnnotation types.OptionalBool + // MetadataFile is the name of a file to which the builder should write a JSON map + // containing metadata about the built image. + MetadataFile string } diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go index 022634ce55..a1f3946f4d 100644 --- a/vendor/github.com/containers/buildah/define/types.go +++ b/vendor/github.com/containers/buildah/define/types.go @@ -29,7 +29,7 @@ const ( // identify working containers. Package = "buildah" // Version for the Package. Also used by .packit.sh for Packit builds. - Version = "1.42.0" + Version = "1.43.0-dev" // DefaultRuntime if containers.conf fails. DefaultRuntime = "runc" diff --git a/vendor/github.com/containers/buildah/docker/types.go b/vendor/github.com/containers/buildah/docker/types.go index 352ae4799f..1619109131 100644 --- a/vendor/github.com/containers/buildah/docker/types.go +++ b/vendor/github.com/containers/buildah/docker/types.go @@ -257,3 +257,10 @@ type V2S2Manifest struct { // configuration. Layers []V2S2Descriptor `json:"layers"` } + +const ( + // github.com/moby/buildkit/exporter/containerimage/exptypes/types.go + ExporterImageDigestKey = "containerimage.digest" + ExporterImageConfigDigestKey = "containerimage.config.digest" + ExporterImageDescriptorKey = "containerimage.descriptor" +) diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index 9bfc41d146..57c7f4708b 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -83,8 +83,16 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B if len(paths) == 0 { return "", nil, errors.New("building: no dockerfiles specified") } - if len(options.Platforms) > 1 && options.IIDFile != "" { - return "", nil, fmt.Errorf("building multiple images, but iidfile %q can only be used to store one image ID", options.IIDFile) + if len(options.Platforms) > 1 { + if options.IIDFile != "" { + return "", nil, fmt.Errorf("building multiple images, but iidfile %q can only be used to store one image ID", options.IIDFile) + } + if options.IIDFileRaw != "" { + return "", nil, fmt.Errorf("building multiple images, but iidfile-raw %q can only be used to store one image ID", options.IIDFileRaw) + } + if options.MetadataFile != "" { + return "", nil, fmt.Errorf("building multiple images, but metadata file %q can only be used to store information about one image", options.MetadataFile) + } } logger := logrus.New() @@ -281,6 +289,12 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B } builds.Go(func() error { + contextDirectory, processLabel, mountLabel, usingContextOverlay, cleanupOverlay, err := platformSetupContextDirectoryOverlay(store, &options) + if err != nil { + return fmt.Errorf("mounting an overlay over build context directory: %w", err) + } + defer cleanupOverlay() + platformOptions.ContextDirectory = contextDirectory loggerPerPlatform := logger if platformOptions.LogFile != "" && platformOptions.LogSplitByPlatform { logFile := platformOptions.LogFile + "_" + platformOptions.OS + "_" + platformOptions.Architecture @@ -299,7 +313,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B platformOptions.ReportWriter = reporter platformOptions.Err = stderr } - thisID, thisRef, err := buildDockerfilesOnce(ctx, store, loggerPerPlatform, logPrefix, platformOptions, paths, files) + thisID, thisRef, err := buildDockerfilesOnce(ctx, store, loggerPerPlatform, logPrefix, platformOptions, paths, files, processLabel, mountLabel, usingContextOverlay) if err != nil { if errorContext := strings.TrimSpace(logPrefix); errorContext != "" { return fmt.Errorf("%s: %w", errorContext, err) @@ -410,7 +424,7 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B return id, ref, nil } -func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logrus.Logger, logPrefix string, options define.BuildOptions, containerFiles []string, dockerfilecontents [][]byte) (string, reference.Canonical, error) { +func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logrus.Logger, logPrefix string, options define.BuildOptions, containerFiles []string, dockerfilecontents [][]byte, processLabel, mountLabel string, usingContextOverlay bool) (string, reference.Canonical, error) { mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0])) if err != nil { return "", nil, fmt.Errorf("parsing main Dockerfile: %s: %w", containerFiles[0], err) @@ -451,7 +465,7 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr mainNode.Children = append(mainNode.Children, additionalNode.Children...) } - exec, err := newExecutor(logger, logPrefix, store, options, mainNode, containerFiles) + exec, err := newExecutor(logger, logPrefix, store, options, mainNode, containerFiles, processLabel, mountLabel, usingContextOverlay) if err != nil { return "", nil, fmt.Errorf("creating build executor: %w", err) } diff --git a/vendor/github.com/containers/buildah/imagebuildah/build_linux.go b/vendor/github.com/containers/buildah/imagebuildah/build_linux.go new file mode 100644 index 0000000000..50ce8ab1e6 --- /dev/null +++ b/vendor/github.com/containers/buildah/imagebuildah/build_linux.go @@ -0,0 +1,86 @@ +package imagebuildah + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "slices" + + "github.com/containers/buildah/define" + "github.com/containers/buildah/internal/tmpdir" + "github.com/containers/buildah/pkg/overlay" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/sirupsen/logrus" + "go.podman.io/storage" + "golang.org/x/sys/unix" +) + +// platformSetupContextDirectoryOverlay() sets up an overlay _over_ the build +// context directory, and sorts out labeling. Returns the location which +// should be used as the default build context; the process label and mount +// label for the build, if any; a boolean value that indicates whether we did, +// in fact, mount an overlay; and a cleanup function which should be called +// when the location is no longer needed (on success). Returned errors should +// be treated as fatal. +func platformSetupContextDirectoryOverlay(store storage.Store, options *define.BuildOptions) (string, string, string, bool, func(), error) { + var succeeded bool + var tmpDir, contentDir string + cleanup := func() { + if contentDir != "" { + if err := overlay.CleanupContent(tmpDir); err != nil { + logrus.Debugf("cleaning up overlay scaffolding for build context directory: %v", err) + } + } + if tmpDir != "" { + if err := os.Remove(tmpDir); err != nil && !errors.Is(err, fs.ErrNotExist) { + logrus.Debugf("removing should-be-empty temporary directory %q: %v", tmpDir, err) + } + } + } + defer func() { + if !succeeded { + cleanup() + } + }() + // double-check that the context directory location is an absolute path + contextDirectoryAbsolute, err := filepath.Abs(options.ContextDirectory) + if err != nil { + return "", "", "", false, nil, fmt.Errorf("determining absolute path of %q: %w", options.ContextDirectory, err) + } + var st unix.Stat_t + if err := unix.Stat(contextDirectoryAbsolute, &st); err != nil { + return "", "", "", false, nil, fmt.Errorf("checking ownership of %q: %w", options.ContextDirectory, err) + } + // figure out the labeling situation + processLabel, mountLabel, err := label.InitLabels(options.CommonBuildOpts.LabelOpts) + if err != nil { + return "", "", "", false, nil, err + } + // create a temporary directory + tmpDir, err = os.MkdirTemp(tmpdir.GetTempDir(), "buildah-context-") + if err != nil { + return "", "", "", false, nil, fmt.Errorf("creating temporary directory: %w", err) + } + // create the scaffolding for an overlay mount under it + contentDir, err = overlay.TempDir(tmpDir, 0, 0) + if err != nil { + return "", "", "", false, nil, fmt.Errorf("creating overlay scaffolding for build context directory: %w", err) + } + // mount an overlay that uses it as a lower + overlayOptions := overlay.Options{ + GraphOpts: slices.Clone(store.GraphOptions()), + ForceMount: true, + MountLabel: mountLabel, + } + targetDir := filepath.Join(contentDir, "target") + contextDirMountSpec, err := overlay.MountWithOptions(contentDir, contextDirectoryAbsolute, targetDir, &overlayOptions) + if err != nil { + return "", "", "", false, nil, fmt.Errorf("creating overlay scaffolding for build context directory: %w", err) + } + // going forward, pretend that the merged directory is the actual context directory + logrus.Debugf("mounted an overlay at %q over %q", contextDirMountSpec.Source, contextDirectoryAbsolute) + succeeded = true + return contextDirMountSpec.Source, processLabel, mountLabel, true, cleanup, nil +} diff --git a/vendor/github.com/containers/buildah/imagebuildah/build_notlinux.go b/vendor/github.com/containers/buildah/imagebuildah/build_notlinux.go new file mode 100644 index 0000000000..37a66e1114 --- /dev/null +++ b/vendor/github.com/containers/buildah/imagebuildah/build_notlinux.go @@ -0,0 +1,20 @@ +//go:build !linux + +package imagebuildah + +import ( + "github.com/containers/buildah/define" + "go.podman.io/storage" +) + +// platformSetupContextDirectoryOverlay() should set up an overlay _over_ the +// build context directory, and sort out labeling. Should return the location +// which should be used as the default build context; the process label and +// mount label for the build, if any; a boolean value that indicates whether we +// did, in fact, mount an overlay; and a cleanup function which should be +// called when the location is no longer needed (on success). Returned errors +// should be treated as fatal. +// TODO: currenty a no-op on this platform. +func platformSetupContextDirectoryOverlay(store storage.Store, options *define.BuildOptions) (string, string, string, bool, func(), error) { + return options.ContextDirectory, "", "", false, func() {}, nil +} diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go index 9f4c707df0..2ebb418f15 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -2,6 +2,7 @@ package imagebuildah import ( "context" + "encoding/json" "errors" "fmt" "io" @@ -15,6 +16,7 @@ import ( "github.com/containers/buildah" "github.com/containers/buildah/define" "github.com/containers/buildah/internal" + "github.com/containers/buildah/internal/metadata" internalUtil "github.com/containers/buildah/internal/util" "github.com/containers/buildah/pkg/parse" "github.com/containers/buildah/pkg/sshagent" @@ -59,18 +61,19 @@ var builtinAllowedBuildArgs = map[string]struct{}{ internal.SourceDateEpochName: {}, } -// Executor is a buildah-based implementation of the imagebuilder.Executor +// executor is a buildah-based implementation of the imagebuilder.Executor // interface. It coordinates the entire build by using one or more -// StageExecutors to handle each stage of the build. -type Executor struct { +// stageExecutors to handle each stage of the build. +type executor struct { cacheFrom []reference.Named cacheTo []reference.Named cacheTTL time.Duration containerSuffix string logger *logrus.Logger - stages map[string]*StageExecutor + stages map[string]*stageExecutor store storage.Store contextDir string + contextDirWritesAreDiscarded bool pullPolicy define.PullPolicy registry string ignoreUnrecognizedInstructions bool @@ -103,6 +106,7 @@ type Executor struct { commonBuildOptions *define.CommonBuildOptions defaultMountsFilePath string iidfile string + iidfileRaw string squash bool labels []string layerLabels []string @@ -172,6 +176,7 @@ type Executor struct { sourceDateEpoch *time.Time rewriteTimestamp bool createdAnnotation types.OptionalBool + metadataFile string } type imageTypeAndHistoryAndDiffIDs struct { @@ -184,7 +189,7 @@ type imageTypeAndHistoryAndDiffIDs struct { } // newExecutor creates a new instance of the imagebuilder.Executor interface. -func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, options define.BuildOptions, mainNode *parser.Node, containerFiles []string) (*Executor, error) { +func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, options define.BuildOptions, mainNode *parser.Node, containerFiles []string, processLabel, mountLabel string, contextWritesDiscarded bool) (*executor, error) { defaultContainerConfig, err := config.Default() if err != nil { return nil, fmt.Errorf("failed to get container config: %w", err) @@ -244,16 +249,17 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o buildOutputs = append(buildOutputs, options.BuildOutput) //nolint:staticcheck } - exec := Executor{ + exec := executor{ args: options.Args, cacheFrom: options.CacheFrom, cacheTo: options.CacheTo, cacheTTL: options.CacheTTL, containerSuffix: options.ContainerSuffix, logger: logger, - stages: make(map[string]*StageExecutor), + stages: make(map[string]*stageExecutor), store: store, contextDir: options.ContextDirectory, + contextDirWritesAreDiscarded: contextWritesDiscarded, excludes: excludes, groupAdd: options.GroupAdd, ignoreFile: options.IgnoreFile, @@ -288,9 +294,12 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o commonBuildOptions: options.CommonBuildOpts, defaultMountsFilePath: options.DefaultMountsFilePath, iidfile: options.IIDFile, + iidfileRaw: options.IIDFileRaw, squash: options.Squash, labels: slices.Clone(options.Labels), layerLabels: slices.Clone(options.LayerLabels), + processLabel: processLabel, + mountLabel: mountLabel, annotations: slices.Clone(options.Annotations), layers: options.Layers, noHostname: options.CommonBuildOpts.NoHostname, @@ -346,6 +355,7 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o sourceDateEpoch: options.SourceDateEpoch, rewriteTimestamp: options.RewriteTimestamp, createdAnnotation: options.CreatedAnnotation, + metadataFile: options.MetadataFile, } // sort unsetAnnotations because we will later write these // values to the history of the image therefore we want to @@ -399,10 +409,10 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o // startStage creates a new stage executor that will be referenced whenever a // COPY or ADD statement uses a --from=NAME flag. -func (b *Executor) startStage(ctx context.Context, stage *imagebuilder.Stage, stages imagebuilder.Stages, output string) *StageExecutor { +func (b *executor) startStage(ctx context.Context, stage *imagebuilder.Stage, stages imagebuilder.Stages, output string) *stageExecutor { // create a copy of systemContext for each stage executor. systemContext := *b.systemContext - stageExec := &StageExecutor{ + stageExec := &stageExecutor{ ctx: ctx, executor: b, systemContext: &systemContext, @@ -423,7 +433,7 @@ func (b *Executor) startStage(ctx context.Context, stage *imagebuilder.Stage, st } // resolveNameToImageRef creates a types.ImageReference for the output name in local storage -func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, error) { +func (b *executor) resolveNameToImageRef(output string) (types.ImageReference, error) { if imageRef, err := alltransports.ParseImageName(output); err == nil { return imageRef, nil } @@ -443,7 +453,7 @@ func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, e // that the specified stage has finished. If there is no stage defined by that // name, then it will return (false, nil). If there is a stage defined by that // name, it will return true along with any error it encounters. -func (b *Executor) waitForStage(ctx context.Context, name string, stages imagebuilder.Stages) (bool, error) { +func (b *executor) waitForStage(ctx context.Context, name string, stages imagebuilder.Stages) (bool, error) { found := false for _, otherStage := range stages { if otherStage.Name == name || strconv.Itoa(otherStage.Position) == name { @@ -479,7 +489,7 @@ func (b *Executor) waitForStage(ctx context.Context, name string, stages imagebu } // getImageTypeAndHistoryAndDiffIDs returns the os, architecture, manifest type, history, and diff IDs list of imageID. -func (b *Executor) getImageTypeAndHistoryAndDiffIDs(ctx context.Context, imageID string) (string, string, string, []v1.History, []digest.Digest, error) { +func (b *executor) getImageTypeAndHistoryAndDiffIDs(ctx context.Context, imageID string) (string, string, string, []v1.History, []digest.Digest, error) { b.imageInfoLock.Lock() imageInfo, ok := b.imageInfoCache[imageID] b.imageInfoLock.Unlock() @@ -519,7 +529,7 @@ func (b *Executor) getImageTypeAndHistoryAndDiffIDs(ctx context.Context, imageID return oci.OS, oci.Architecture, manifestFormat, oci.History, oci.RootFS.DiffIDs, nil } -func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageExecutor, stages imagebuilder.Stages, stageIndex int) (imageID string, ref reference.Canonical, onlyBaseImage bool, err error) { +func (b *executor) buildStage(ctx context.Context, cleanupStages map[int]*stageExecutor, stages imagebuilder.Stages, stageIndex int) (imageID string, commitResults *buildah.CommitResults, onlyBaseImage bool, err error) { stage := stages[stageIndex] ib := stage.Builder node := stage.Node @@ -616,7 +626,7 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE } // Build this stage. - if imageID, ref, onlyBaseImage, err = stageExecutor.Execute(ctx, base); err != nil { + if imageID, commitResults, onlyBaseImage, err = stageExecutor.execute(ctx, base); err != nil { return "", nil, onlyBaseImage, err } @@ -630,7 +640,7 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE b.stagesLock.Unlock() } - return imageID, ref, onlyBaseImage, nil + return imageID, commitResults, onlyBaseImage, nil } type stageDependencyInfo struct { @@ -652,7 +662,7 @@ func markDependencyStagesForTarget(dependencyMap map[string]*stageDependencyInfo } } -func (b *Executor) warnOnUnsetBuildArgs(stages imagebuilder.Stages, dependencyMap map[string]*stageDependencyInfo, args map[string]string) { +func (b *executor) warnOnUnsetBuildArgs(stages imagebuilder.Stages, dependencyMap map[string]*stageDependencyInfo, args map[string]string) { argFound := make(map[string]struct{}) for _, stage := range stages { node := stage.Node // first line @@ -699,12 +709,12 @@ func (b *Executor) warnOnUnsetBuildArgs(stages imagebuilder.Stages, dependencyMa // Build takes care of the details of running Prepare/Execute/Commit/Delete // over each of the one or more parsed Dockerfiles and stages. -func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) { +func (b *executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) { if len(stages) == 0 { return "", nil, errors.New("building: no stages to build") } var cleanupImages []string - cleanupStages := make(map[int]*StageExecutor) + cleanupStages := make(map[int]*stageExecutor) stdout := b.out if b.quiet { @@ -922,7 +932,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image Index int ImageID string OnlyBaseImage bool - Ref reference.Canonical + CommitResults buildah.CommitResults Error error } @@ -935,6 +945,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image var wg sync.WaitGroup wg.Add(len(stages)) + var commitResults buildah.CommitResults go func() { cancel := false for stageIndex := range stages { @@ -983,7 +994,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image return } } - stageID, stageRef, stageOnlyBaseImage, stageErr := b.buildStage(ctx, cleanupStages, stages, index) + stageID, stageResults, stageOnlyBaseImage, stageErr := b.buildStage(ctx, cleanupStages, stages, index) if stageErr != nil { cancel = true ch <- Result{ @@ -997,7 +1008,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image ch <- Result{ Index: index, ImageID: stageID, - Ref: stageRef, + CommitResults: *stageResults, OnlyBaseImage: stageOnlyBaseImage, Error: nil, } @@ -1037,7 +1048,8 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image } if r.Index == len(stages)-1 { imageID = r.ImageID - ref = r.Ref + commitResults = r.CommitResults + ref = commitResults.Canonical } b.stagesLock.Unlock() } @@ -1088,23 +1100,56 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image if b.iidfile != "" { iid := imageID if iid != "" { - iid = "sha256:" + iid // only prepend a digest algorithm name if we actually got a value back + cdigest, err := digest.Parse("sha256:" + imageID) + if err != nil { + return imageID, ref, fmt.Errorf("coercing image ID into a digest structure: %w", err) + } + iid = cdigest.String() } if err = os.WriteFile(b.iidfile, []byte(iid), 0o644); err != nil { return imageID, ref, fmt.Errorf("failed to write image ID to file %q: %w", b.iidfile, err) } - } else { + } + if b.iidfileRaw != "" { + if err = os.WriteFile(b.iidfileRaw, []byte(imageID), 0o644); err != nil { + return imageID, ref, fmt.Errorf("failed to write image ID to file %q: %w", b.iidfileRaw, err) + } + } + if b.iidfile == "" && b.iidfileRaw == "" { if _, err := stdout.Write([]byte(imageID + "\n")); err != nil { return imageID, ref, fmt.Errorf("failed to write image ID to stdout: %w", err) } } + if b.metadataFile != "" { + var cdigest digest.Digest + if imageID != "" { + if cdigest, err = digest.Parse("sha256:" + imageID); err != nil { + return imageID, ref, fmt.Errorf("coercing image ID into a digest structure: %w", err) + } + } + metadata, err := metadata.Build(cdigest, v1.Descriptor{ + MediaType: commitResults.MediaType, + Digest: commitResults.Digest, + Size: int64(len(commitResults.ImageManifest)), + }) + if err != nil { + return imageID, ref, fmt.Errorf("building metadata for metadata file: %w", err) + } + metadataBytes, err := json.Marshal(metadata) + if err != nil { + return imageID, ref, fmt.Errorf("encoding metadata for metadata file: %w", err) + } + if err = os.WriteFile(b.metadataFile, metadataBytes, 0o644); err != nil { + return imageID, ref, fmt.Errorf("failed to write image metadata to file %q: %w", b.metadataFile, err) + } + } return imageID, ref, nil } // deleteSuccessfulIntermediateCtrs goes through the container IDs in each // stage's containerIDs list and deletes the containers associated with those // IDs. -func (b *Executor) deleteSuccessfulIntermediateCtrs() error { +func (b *executor) deleteSuccessfulIntermediateCtrs() error { var lastErr error for _, s := range b.stages { for _, ctr := range s.containerIDs { diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go index 7a57d80eff..876e558f53 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go @@ -19,6 +19,8 @@ import ( "github.com/containers/buildah/define" buildahdocker "github.com/containers/buildah/docker" "github.com/containers/buildah/internal" + "github.com/containers/buildah/internal/metadata" + "github.com/containers/buildah/internal/sanitize" "github.com/containers/buildah/internal/tmpdir" internalUtil "github.com/containers/buildah/internal/util" "github.com/containers/buildah/pkg/parse" @@ -37,6 +39,7 @@ import ( cp "go.podman.io/image/v5/copy" imagedocker "go.podman.io/image/v5/docker" "go.podman.io/image/v5/docker/reference" + "go.podman.io/image/v5/image" "go.podman.io/image/v5/manifest" is "go.podman.io/image/v5/storage" "go.podman.io/image/v5/transports" @@ -46,7 +49,7 @@ import ( "go.podman.io/storage/pkg/unshare" ) -// StageExecutor bundles up what we need to know when executing one stage of a +// stageExecutor bundles up what we need to know when executing one stage of a // (possibly multi-stage) build. // Each stage may need to produce an image to be used as the base in a later // stage (with the last stage's image being the end product of the build), and @@ -57,10 +60,10 @@ import ( // and set of volumes. // If we're naming the result of the build, only the last stage will apply that // name to the image that it produces. -type StageExecutor struct { +type stageExecutor struct { ctx context.Context systemContext *types.SystemContext - executor *Executor + executor *executor log func(format string, args ...any) index int stages imagebuilder.Stages @@ -83,7 +86,7 @@ type StageExecutor struct { // Preserve informs the stage executor that from this point on, it needs to // ensure that only COPY and ADD instructions can modify the contents of this // directory or anything below it. -// When CompatVolumes is enabled, the StageExecutor handles this by caching the +// When CompatVolumes is enabled, the stageExecutor handles this by caching the // contents of directories which have been marked this way before executing a // RUN instruction, invalidating that cache when an ADD or COPY instruction // sets any location under the directory as the destination, and using the @@ -93,7 +96,7 @@ type StageExecutor struct { // mount of itself during Run(), but the directory is expected to be remain // writeable while the RUN instruction is being handled, even if any changes // made within the directory are ultimately discarded. -func (s *StageExecutor) Preserve(path string) error { +func (s *stageExecutor) Preserve(path string) error { logrus.Debugf("PRESERVE %q in %q (already preserving %v)", path, s.builder.ContainerID, s.volumes) // Try and resolve the symlink (if one exists) @@ -200,7 +203,7 @@ func (s *StageExecutor) Preserve(path string) error { // Remove any volume cache item which will need to be re-saved because we're // writing to part of it. -func (s *StageExecutor) volumeCacheInvalidate(path string) error { +func (s *stageExecutor) volumeCacheInvalidate(path string) error { invalidated := []string{} for cachedPath := range s.volumeCache { if strings.HasPrefix(path, cachedPath+string(os.PathSeparator)) { @@ -222,7 +225,7 @@ func (s *StageExecutor) volumeCacheInvalidate(path string) error { // Save the contents of each of the executor's list of volumes for which we // don't already have a cache file. -func (s *StageExecutor) volumeCacheSaveVFS() (mounts []specs.Mount, err error) { +func (s *stageExecutor) volumeCacheSaveVFS() (mounts []specs.Mount, err error) { for cachedPath, cacheFile := range s.volumeCache { archivedPath, err := copier.Eval(s.mountPoint, filepath.Join(s.mountPoint, cachedPath), copier.EvalOptions{}) if err != nil { @@ -270,7 +273,7 @@ func (s *StageExecutor) volumeCacheSaveVFS() (mounts []specs.Mount, err error) { } // Restore the contents of each of the executor's list of volumes. -func (s *StageExecutor) volumeCacheRestoreVFS() (err error) { +func (s *stageExecutor) volumeCacheRestoreVFS() (err error) { for cachedPath, cacheFile := range s.volumeCache { archivedPath, err := copier.Eval(s.mountPoint, filepath.Join(s.mountPoint, cachedPath), copier.EvalOptions{}) if err != nil { @@ -314,7 +317,7 @@ func (s *StageExecutor) volumeCacheRestoreVFS() (err error) { // don't already have a cache file. For overlay, we "save" and "restore" by // using it as a lower for an overlay mount in the same location, and then // discarding the upper. -func (s *StageExecutor) volumeCacheSaveOverlay() (mounts []specs.Mount, err error) { +func (s *stageExecutor) volumeCacheSaveOverlay() (mounts []specs.Mount, err error) { for cachedPath := range s.volumeCache { volumePath := filepath.Join(s.mountPoint, cachedPath) mount := specs.Mount{ @@ -328,13 +331,13 @@ func (s *StageExecutor) volumeCacheSaveOverlay() (mounts []specs.Mount, err erro } // Reset the contents of each of the executor's list of volumes. -func (s *StageExecutor) volumeCacheRestoreOverlay() error { +func (s *stageExecutor) volumeCacheRestoreOverlay() error { return nil } // Save the contents of each of the executor's list of volumes for which we // don't already have a cache file. -func (s *StageExecutor) volumeCacheSave() (mounts []specs.Mount, err error) { +func (s *stageExecutor) volumeCacheSave() (mounts []specs.Mount, err error) { switch s.executor.store.GraphDriverName() { case "overlay": return s.volumeCacheSaveOverlay() @@ -343,7 +346,7 @@ func (s *StageExecutor) volumeCacheSave() (mounts []specs.Mount, err error) { } // Reset the contents of each of the executor's list of volumes. -func (s *StageExecutor) volumeCacheRestore() error { +func (s *stageExecutor) volumeCacheRestore() error { switch s.executor.store.GraphDriverName() { case "overlay": return s.volumeCacheRestoreOverlay() @@ -353,7 +356,7 @@ func (s *StageExecutor) volumeCacheRestore() error { // Copy copies data into the working tree. The "Download" field is how // imagebuilder tells us the instruction was "ADD" and not "COPY". -func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) error { +func (s *stageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) error { for _, cp := range copies { if cp.KeepGitDir { if cp.Download { @@ -375,7 +378,7 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err return s.performCopy(excludes, copies...) } -func (s *StageExecutor) performCopy(excludes []string, copies ...imagebuilder.Copy) error { +func (s *stageExecutor) performCopy(excludes []string, copies ...imagebuilder.Copy) error { copiesExtend := []imagebuilder.Copy{} for _, copy := range copies { if err := s.volumeCacheInvalidate(copy.Dest); err != nil { @@ -451,7 +454,7 @@ func (s *StageExecutor) performCopy(excludes []string, copies ...imagebuilder.Co copy.Src = copySources } - if len(copy.From) > 0 && len(copy.Files) == 0 { + if copy.From != "" && len(copy.Files) == 0 { // If from has an argument within it, resolve it to its // value. Otherwise just return the value found. from, fromErr := imagebuilder.ProcessWord(copy.From, s.stage.Builder.Arguments()) @@ -533,7 +536,8 @@ func (s *StageExecutor) performCopy(excludes []string, copies ...imagebuilder.Co } contextDir = mountPoint } - // Original behaviour of buildah still stays true for COPY irrespective of additional context. + // With --from set, the content being copied isn't coming from the default + // build context directory, so we're not expected to force everything to 0:0 preserveOwnership = true copyExcludes = excludes } else { @@ -616,9 +620,14 @@ func (s *StageExecutor) performCopy(excludes []string, copies ...imagebuilder.Co } // Returns a map of StageName/ImageName:internal.StageMountDetails for the -// items in the passed-in mounts list which include a "from=" value. -func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]internal.StageMountDetails, error) { +// items in the passed-in mounts list which include a "from=" value. The "" +// key in the returned map corresponds to the default build context. +func (s *stageExecutor) runStageMountPoints(mountList []string) (map[string]internal.StageMountDetails, error) { stageMountPoints := make(map[string]internal.StageMountDetails) + stageMountPoints[""] = internal.StageMountDetails{ + MountPoint: s.executor.contextDir, + IsWritesDiscardedOverlay: s.executor.contextDirWritesAreDiscarded, + } for _, flag := range mountList { if strings.Contains(flag, "from") { tokens := strings.Split(flag, ",") @@ -648,7 +657,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte if additionalBuildContext.IsImage { mountPoint, err := s.getImageRootfs(s.ctx, additionalBuildContext.Value) if err != nil { - return nil, fmt.Errorf("%s from=%s: image found with that name", flag, from) + return nil, fmt.Errorf("%s from=%s: image not found with that name", flag, from) } // The `from` in stageMountPoints should point // to `mountPoint` replaced from additional @@ -733,7 +742,7 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte return stageMountPoints, nil } -func (s *StageExecutor) createNeededHeredocMountsForRun(files []imagebuilder.File) ([]Mount, error) { +func (s *stageExecutor) createNeededHeredocMountsForRun(files []imagebuilder.File) ([]Mount, error) { mountResult := []Mount{} for _, file := range files { f, err := os.CreateTemp(parse.GetTempDir(), "buildahheredoc") @@ -769,7 +778,7 @@ func parseSheBang(data string) string { // Run executes a RUN instruction using the stage's current working container // as a root directory. -func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { +func (s *stageExecutor) Run(run imagebuilder.Run, config docker.Config) error { logrus.Debugf("RUN %#v, %#v", run, config) args := run.Args heredocMounts := []Mount{} @@ -910,7 +919,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { // UnrecognizedInstruction is called when we encounter an instruction that the // imagebuilder parser didn't understand. -func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error { +func (s *stageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error { errStr := fmt.Sprintf("Build error: Unknown instruction: %q ", strings.ToUpper(step.Command)) err := fmt.Sprintf(errStr+"%#v", step) if s.executor.ignoreUnrecognizedInstructions { @@ -930,10 +939,33 @@ func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error { return errors.New(err) } +// sanitizeFrom limits which image names (with or without transport prefixes) +// we'll accept. For those it accepts which refer to filesystem objects, where +// relative path names are evaluated relative to "contextDir", it will create a +// copy of the original image, under "tmpdir", which contains no symbolic +// links, and return either the original image reference or a reference to a +// sanitized copy which should be used instead. +func (s *stageExecutor) sanitizeFrom(from, tmpdir string) (newFrom string, err error) { + transportName, restOfImageName, maybeHasTransportName := strings.Cut(from, ":") + if !maybeHasTransportName || transports.Get(transportName) == nil { + if _, err = reference.ParseNormalizedNamed(from); err == nil { + // this is a normal-looking image-in-a-registry-or-named-in-storage name + return from, nil + } + if img, err := s.executor.store.Image(from); img != nil && err == nil { + // this is an image ID + return from, nil + } + return "", fmt.Errorf("parsing image name %q: %w", from, err) + } + // TODO: drop this part and just return an error... someday + return sanitize.ImageName(transportName, restOfImageName, s.executor.contextDir, tmpdir) +} + // prepare creates a working container based on the specified image, or if one // isn't specified, the first argument passed to the first FROM instruction we // can find in the stage's parsed tree. -func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBConfig, rebase, preserveBaseImageAnnotations bool, pullPolicy define.PullPolicy) (builder *buildah.Builder, err error) { +func (s *stageExecutor) prepare(ctx context.Context, from string, initializeIBConfig, rebase, preserveBaseImageAnnotations bool, pullPolicy define.PullPolicy) (builder *buildah.Builder, err error) { stage := s.stage ib := stage.Builder node := stage.Node @@ -946,6 +978,10 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo } from = base } + sanitizedFrom, err := s.sanitizeFrom(from, tmpdir.GetTempDir()) + if err != nil { + return nil, fmt.Errorf("invalid base image specification %q: %w", from, err) + } displayFrom := from if ib.Platform != "" { displayFrom = "--platform=" + ib.Platform + " " + displayFrom @@ -985,7 +1021,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo builderOptions := buildah.BuilderOptions{ Args: ib.Args, - FromImage: from, + FromImage: sanitizedFrom, GroupAdd: s.executor.groupAdd, PullPolicy: pullPolicy, ContainerSuffix: s.executor.containerSuffix, @@ -1023,16 +1059,6 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo return nil, fmt.Errorf("creating build container: %w", err) } - // If executor's ProcessLabel and MountLabel is empty means this is the first stage - // Make sure we share first stage's ProcessLabel and MountLabel with all other subsequent stages - // Doing this will ensure and one stage in same build can mount another stage even if `selinux` - // is enabled. - - if s.executor.mountLabel == "" && s.executor.processLabel == "" { - s.executor.mountLabel = builder.MountLabel - s.executor.processLabel = builder.ProcessLabel - } - if initializeIBConfig { volumes := map[string]struct{}{} for _, v := range builder.Volumes() { @@ -1121,7 +1147,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo } // Delete deletes the stage's working container, if we have one. -func (s *StageExecutor) Delete() (err error) { +func (s *stageExecutor) Delete() (err error) { if s.builder != nil { err = s.builder.Delete() s.builder = nil @@ -1131,7 +1157,7 @@ func (s *StageExecutor) Delete() (err error) { // stepRequiresLayer indicates whether or not the step should be followed by // committing a layer container when creating an intermediate image. -func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool { +func (*stageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool { switch strings.ToUpper(step.Command) { case "ADD", "COPY", "RUN": return true @@ -1143,7 +1169,7 @@ func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool { // storage. If it isn't found, it pulls down a copy. Then, if we don't have a // working container root filesystem based on the image, it creates one. Then // it returns that root filesystem's location. -func (s *StageExecutor) getImageRootfs(ctx context.Context, image string) (mountPoint string, err error) { +func (s *stageExecutor) getImageRootfs(ctx context.Context, image string) (mountPoint string, err error) { if builder, ok := s.executor.containerMap[image]; ok { return builder.MountPoint, nil } @@ -1158,7 +1184,7 @@ func (s *StageExecutor) getImageRootfs(ctx context.Context, image string) (mount // getContentSummary generates a description of what was most recently added to the container, // typically in the form "file", "dir", or "multi" followed by a colon and the hex part of the // digest of the content, for inclusion in the corresponding history entry's "createdBy" field -func (s *StageExecutor) getContentSummaryAfterAddingContent() string { +func (s *stageExecutor) getContentSummaryAfterAddingContent() string { contentType, digest := s.builder.ContentDigester.Digest() summary := contentType if digest != "" { @@ -1172,7 +1198,7 @@ func (s *StageExecutor) getContentSummaryAfterAddingContent() string { } // Execute runs each of the steps in the stage's parsed tree, in turn. -func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, ref reference.Canonical, onlyBaseImg bool, err error) { +func (s *stageExecutor) execute(ctx context.Context, base string) (imgID string, commitResults *buildah.CommitResults, onlyBaseImg bool, err error) { var resourceUsage rusage.Rusage stage := s.stage ib := stage.Builder @@ -1311,7 +1337,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if err != nil { return "", nil, false, fmt.Errorf("unable to get createdBy for the node: %w", err) } - if imgID, ref, err = s.commit(ctx, createdBy, emptyLayer, s.output, s.executor.squash || s.executor.confidentialWorkload.Convert, lastStage); err != nil { + if imgID, commitResults, err = s.commit(ctx, createdBy, emptyLayer, s.output, s.executor.squash || s.executor.confidentialWorkload.Convert, lastStage); err != nil { return "", nil, false, fmt.Errorf("committing base container: %w", err) } } else { @@ -1320,7 +1346,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // the command line options, so just reuse the base // image. logCommit(s.output, -1) - if imgID, ref, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil { + if imgID, commitResults, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil { return "", nil, onlyBaseImage, err } onlyBaseImage = true @@ -1478,7 +1504,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if err != nil { return "", nil, false, fmt.Errorf("unable to get createdBy for the node: %w", err) } - imgID, ref, err = s.commit(ctx, createdBy, false, s.output, s.executor.squash, lastStage && lastInstruction) + imgID, commitResults, err = s.commit(ctx, createdBy, false, s.output, s.executor.squash, lastStage && lastInstruction) if err != nil { return "", nil, false, fmt.Errorf("committing container for step %+v: %w", *step, err) } @@ -1491,6 +1517,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, } } else { imgID = "" + commitResults = &buildah.CommitResults{} } break } @@ -1691,9 +1718,9 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, imgID = cacheID if commitName != "" { logCommit(commitName, i) - if imgID, ref, err = s.tagExistingImage(ctx, cacheID, commitName); err != nil { - return "", nil, false, err - } + } + if imgID, commitResults, err = s.tagExistingImage(ctx, cacheID, commitName); err != nil { + return "", nil, false, err } } else { // We're not going to find any more cache hits, so we @@ -1710,7 +1737,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // because at this point we want to save history for // layers even if its a squashed build so that they // can be part of the build cache. - imgID, ref, err = s.commit(ctx, createdBy, !s.stepRequiresLayer(step), commitName, false, lastStage && lastInstruction) + imgID, commitResults, err = s.commit(ctx, createdBy, !s.stepRequiresLayer(step), commitName, false, lastStage && lastInstruction) if err != nil { return "", nil, false, fmt.Errorf("committing container for step %+v: %w", *step, err) } @@ -1750,7 +1777,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // version of the image if that's what we're after, // or a normal one if we need to scan the image while // committing it. - imgID, ref, err = s.commit(ctx, createdBy, !s.stepRequiresLayer(step), commitName, s.executor.squash || s.executor.confidentialWorkload.Convert, lastStage && lastInstruction) + imgID, commitResults, err = s.commit(ctx, createdBy, !s.stepRequiresLayer(step), commitName, s.executor.squash || s.executor.confidentialWorkload.Convert, lastStage && lastInstruction) if err != nil { return "", nil, false, fmt.Errorf("committing final squash step %+v: %w", *step, err) } @@ -1810,7 +1837,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, s.hasLink = false } - return imgID, ref, onlyBaseImage, nil + return imgID, commitResults, onlyBaseImage, nil } func historyEntriesEqual(base, derived v1.History) bool { @@ -1844,7 +1871,7 @@ func historyEntriesEqual(base, derived v1.History) bool { // that we're comparing. // Used to verify whether a cache of the intermediate image exists and whether // to run the build again. -func (s *StageExecutor) historyAndDiffIDsMatch(baseHistory []v1.History, baseDiffIDs []digest.Digest, child *parser.Node, history []v1.History, diffIDs []digest.Digest, addedContentSummary string, buildAddsLayer bool, lastInstruction bool) (bool, error) { +func (s *stageExecutor) historyAndDiffIDsMatch(baseHistory []v1.History, baseDiffIDs []digest.Digest, child *parser.Node, history []v1.History, diffIDs []digest.Digest, addedContentSummary string, buildAddsLayer bool, lastInstruction bool) (bool, error) { // our history should be as long as the base's, plus one entry for what // we're doing if len(history) != len(baseHistory)+1 { @@ -1901,7 +1928,7 @@ func (s *StageExecutor) historyAndDiffIDsMatch(baseHistory []v1.History, baseDif // in the layer, unsetting labels, or anything having to do with annotations) // were made so that a future build won't mistake this result for a cache hit // unless the same flags are being used at that time. -func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary string, isLastStep bool) (string, error) { +func (s *stageExecutor) getCreatedBy(node *parser.Node, addedContentSummary string, isLastStep bool) (string, error) { if node == nil { return "/bin/sh", nil } @@ -2020,7 +2047,7 @@ func (s *StageExecutor) getCreatedBy(node *parser.Node, addedContentSummary stri // it excludes any build-args that were not used in the build process // values for args are overridden by the values specified using ENV. // Reason: Values from ENV will always override values specified arg. -func (s *StageExecutor) getBuildArgsResolvedForRun() string { +func (s *stageExecutor) getBuildArgsResolvedForRun() string { var envs []string configuredEnvs := make(map[string]string) dockerConfig := s.stage.Builder.Config() @@ -2074,7 +2101,7 @@ func (s *StageExecutor) getBuildArgsResolvedForRun() string { // getBuildArgs key returns the set of args which were specified during the // build process, formatted for inclusion in the build history -func (s *StageExecutor) getBuildArgsKey() string { +func (s *stageExecutor) getBuildArgsKey() string { var args []string for key := range s.stage.Builder.Args { if _, ok := s.stage.Builder.AllowedArgs[key]; ok { @@ -2086,59 +2113,108 @@ func (s *StageExecutor) getBuildArgsKey() string { } // tagExistingImage adds names to an image already in the store -func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) { - // If we don't need to attach a name to the image, just return the cache ID. +func (s *stageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, *buildah.CommitResults, error) { + var manifestBytes []byte + var manifestType string + var ref reference.Canonical + var dref reference.Named + + // If we don't need to attach a name to the image, read its manifest back. if output == "" { - return cacheID, nil, nil - } - - // Get the destination image reference. - dest, err := s.executor.resolveNameToImageRef(output) - if err != nil { - return "", nil, err - } - - policyContext, err := util.GetPolicyContext(s.systemContext) - if err != nil { - return "", nil, err - } - defer func() { - if destroyErr := policyContext.Destroy(); destroyErr != nil { - if err == nil { - err = destroyErr - } else { - err = fmt.Errorf("%v: %w", destroyErr.Error(), err) - } + // Look up the source image, expecting it to be in local storage + ref, err := is.Transport.ParseStoreReference(s.executor.store, cacheID) + if err != nil { + return "", nil, fmt.Errorf("getting source imageReference for %q: %w", cacheID, err) + } + srcSrc, err := ref.NewImageSource(ctx, s.systemContext) + if err != nil { + return "", nil, fmt.Errorf("instantiating image for %q: %w", transports.ImageName(ref), err) + } + defer srcSrc.Close() + unparsedTop := image.UnparsedInstance(srcSrc, nil) + // Make sure we have the manifest and its type before continuing. + manifestBytes, manifestType, err = unparsedTop.Manifest(ctx) + if err != nil { + return "", nil, fmt.Errorf("loading image manifest for %q: %w", transports.ImageName(ref), err) + } + } else { + // Get the destination image reference for the name we want to assign. + dest, err := s.executor.resolveNameToImageRef(output) + if err != nil { + return "", nil, err } - }() - // Look up the source image, expecting it to be in local storage - src, err := is.Transport.ParseStoreReference(s.executor.store, cacheID) - if err != nil { - return "", nil, fmt.Errorf("getting source imageReference for %q: %w", cacheID, err) + policyContext, err := util.GetPolicyContext(s.systemContext) + if err != nil { + return "", nil, err + } + defer func() { + if destroyErr := policyContext.Destroy(); destroyErr != nil { + if err == nil { + err = destroyErr + } else { + err = fmt.Errorf("%v: %w", destroyErr.Error(), err) + } + } + }() + + // Look up the source image, expecting it to be in local storage + src, err := is.Transport.ParseStoreReference(s.executor.store, cacheID) + if err != nil { + return "", nil, fmt.Errorf("getting source imageReference for %q: %w", cacheID, err) + } + destinationTimestamp := s.executor.timestamp + if s.executor.sourceDateEpoch != nil { + destinationTimestamp = s.executor.sourceDateEpoch + } + options := cp.Options{ + RemoveSignatures: true, // more like "ignore signatures", since they don't get removed when src and dest are the same image + DestinationTimestamp: destinationTimestamp, + } + // Make sure we have the manifest and its type before continuing. + manifestBytes, err = cp.Image(ctx, policyContext, dest, src, &options) + if err != nil { + return "", nil, fmt.Errorf("copying image %q: %w", cacheID, err) + } + manifestType = manifest.GuessMIMEType(manifestBytes) + // And we can also return a canonical reference, since we had a name to assign. + dref = dest.DockerReference() } - options := cp.Options{ - RemoveSignatures: true, // more like "ignore signatures", since they don't get removed when src and dest are the same image - } - manifestBytes, err := cp.Image(ctx, policyContext, dest, src, &options) + + // Parse and digest the manifest to dig out info about the configuration. + parsedManifest, err := manifest.FromBlob(manifestBytes, manifestType) if err != nil { - return "", nil, fmt.Errorf("copying image %q: %w", cacheID, err) + return "", nil, fmt.Errorf("parsing manifest for image %q: %w", cacheID, err) } manifestDigest, err := manifest.Digest(manifestBytes) if err != nil { return "", nil, fmt.Errorf("computing digest of manifest for image %q: %w", cacheID, err) } - _, img, err := is.ResolveReference(dest) + // Reconstruct the metadata that would be returned if we were committing it fresh. + metadata, err := metadata.Build(parsedManifest.ConfigInfo().Digest, v1.Descriptor{ + MediaType: manifestType, + Digest: manifestDigest, + Size: int64(len(manifestBytes)), + }) if err != nil { - return "", nil, fmt.Errorf("locating new copy of image %q (i.e., %q): %w", cacheID, transports.ImageName(dest), err) + return "", nil, fmt.Errorf("reconstructing metadata for image %q: %w", cacheID, err) } - var ref reference.Canonical - if dref := dest.DockerReference(); dref != nil { + // If we had a name, generate the canonical reference for it. + if dref != nil { if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { - return "", nil, fmt.Errorf("computing canonical reference for new image %q (i.e., %q): %w", cacheID, transports.ImageName(dest), err) + return "", nil, fmt.Errorf("computing canonical reference for new image %q: %w", cacheID, err) } } - return img.ID, ref, nil + // Construct the return values. + commitResults := buildah.CommitResults{ + ImageID: cacheID, + Canonical: ref, + MediaType: manifestType, + ImageManifest: manifestBytes, + Digest: manifestDigest, + Metadata: metadata, + } + return cacheID, &commitResults, nil } // generateCacheKey returns a computed digest for the current STEP @@ -2146,7 +2222,7 @@ func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output st // generated CacheKey is further used by buildah to lock and decide // tag for the intermediate image which can be pushed and pulled to/from // the remote repository. -func (s *StageExecutor) generateCacheKey(ctx context.Context, currNode *parser.Node, addedContentDigest string, buildAddsLayer bool, lastInstruction bool) (string, error) { +func (s *stageExecutor) generateCacheKey(ctx context.Context, currNode *parser.Node, addedContentDigest string, buildAddsLayer bool, lastInstruction bool) (string, error) { hash := sha256.New() var baseHistory []v1.History var diffIDs []digest.Digest @@ -2200,7 +2276,7 @@ func cacheImageReferences(repos []reference.Named, cachekey string) ([]types.Ima // pushCache takes the image id of intermediate image and attempts // to perform push at the remote repository with cacheKey as the tag. // Returns error if fails otherwise returns nil. -func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) error { +func (s *stageExecutor) pushCache(ctx context.Context, src, cacheKey string) error { destList, err := cacheImageReferences(s.executor.cacheTo, cacheKey) if err != nil { return err @@ -2238,7 +2314,7 @@ func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) err // or a newer version of cache was found in the upstream repo. If new // image was pulled function returns image id otherwise returns empty // string "" or error if any error was encontered while pulling the cache. -func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (reference.Named, string, error) { +func (s *stageExecutor) pullCache(ctx context.Context, cacheKey string) (reference.Named, string, error) { srcList, err := cacheImageReferences(s.executor.cacheFrom, cacheKey) if err != nil { return nil, "", err @@ -2279,7 +2355,7 @@ func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (referen // intermediateImageExists returns image ID if an intermediate image of currNode exists in the image store from a previous build. // It verifies this by checking the parent of the top layer of the image and the history. // If more than one image matches as potential candidates then priority is given to the most recently built image. -func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *parser.Node, addedContentDigest string, buildAddsLayer bool, lastInstruction bool) (string, error) { +func (s *stageExecutor) intermediateImageExists(ctx context.Context, currNode *parser.Node, addedContentDigest string, buildAddsLayer bool, lastInstruction bool) (string, error) { cacheCandidates := []storage.Image{} // Get the list of images available in the image store images, err := s.executor.store.Images() @@ -2384,7 +2460,7 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p // commit writes the container's contents to an image, using a passed-in tag as // the name if there is one, generating a unique ID-based one otherwise. // or commit via any custom exporter if specified. -func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash, finalInstruction bool) (string, reference.Canonical, error) { +func (s *stageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash, finalInstruction bool) (string, *buildah.CommitResults, error) { ib := s.stage.Builder var imageRef types.ImageReference if output != "" { @@ -2538,22 +2614,14 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer options.ConfidentialWorkloadOptions = s.executor.confidentialWorkload options.SBOMScanOptions = s.executor.sbomScanOptions } - imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) + results, err := s.builder.CommitResults(ctx, imageRef, options) if err != nil { return "", nil, err } - var ref reference.Canonical - if imageRef != nil { - if dref := imageRef.DockerReference(); dref != nil { - if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { - return "", nil, fmt.Errorf("computing canonical reference for new image %q: %w", imgID, err) - } - } - } - return imgID, ref, nil + return results.ImageID, results, nil } -func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOption) error { +func (s *stageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOption) error { forceTimestamp := s.executor.timestamp if s.executor.sourceDateEpoch != nil { forceTimestamp = s.executor.sourceDateEpoch @@ -2597,12 +2665,12 @@ func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOp return nil } -func (s *StageExecutor) EnsureContainerPath(path string) error { +func (s *stageExecutor) EnsureContainerPath(path string) error { logrus.Debugf("EnsureContainerPath %q in %q", path, s.builder.ContainerID) return s.builder.EnsureContainerPathAs(path, "", nil) } -func (s *StageExecutor) EnsureContainerPathAs(path, user string, mode *os.FileMode) error { +func (s *stageExecutor) EnsureContainerPathAs(path, user string, mode *os.FileMode) error { logrus.Debugf("EnsureContainerPath %q (owner %q, mode %o) in %q", path, user, mode, s.builder.ContainerID) return s.builder.EnsureContainerPathAs(path, user, mode) } @@ -2613,7 +2681,7 @@ func (s *StageExecutor) EnsureContainerPathAs(path, user string, mode *os.FileMo // whether or not it should be used as a cache hit for another build with that // flag set differently should be reflected in its result. Some build settings // only take affect at the final step, so only note those when they're applied. -func (s *StageExecutor) buildMetadata(isLastStep bool, isAddOrCopy bool) string { +func (s *stageExecutor) buildMetadata(isLastStep bool, isAddOrCopy bool) string { unsetLabels := "" inheritLabels := "" unsetAnnotations := "" diff --git a/vendor/github.com/containers/buildah/info.go b/vendor/github.com/containers/buildah/info.go index d0dd48932f..31c9000215 100644 --- a/vendor/github.com/containers/buildah/info.go +++ b/vendor/github.com/containers/buildah/info.go @@ -51,16 +51,11 @@ func hostInfo() map[string]any { info["cpus"] = runtime.NumCPU() info["rootless"] = unshare.IsRootless() - unified, err := cgroups.IsCgroup2UnifiedMode() + _, err := cgroups.IsCgroup2UnifiedMode() if err != nil { logrus.Error(err, "err reading cgroups mode") } - cgroupVersion := "v1" ociruntime := util.Runtime() - if unified { - cgroupVersion = "v2" - } - info["CgroupVersion"] = cgroupVersion info["OCIRuntime"] = ociruntime mi, err := system.ReadMemInfo() diff --git a/vendor/github.com/containers/buildah/internal/metadata/metadata.go b/vendor/github.com/containers/buildah/internal/metadata/metadata.go new file mode 100644 index 0000000000..28b2de4f96 --- /dev/null +++ b/vendor/github.com/containers/buildah/internal/metadata/metadata.go @@ -0,0 +1,22 @@ +package metadata + +import ( + "github.com/containers/buildah/docker" + "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Build constructs a map containing the passed-in information about a just-committed or reused-as-cache image. +func Build(imageConfigDigest digest.Digest, descriptor v1.Descriptor) (map[string]any, error) { + metadata := make(map[string]any) + if imageConfigDigest.Validate() == nil { + metadata[docker.ExporterImageConfigDigestKey] = imageConfigDigest.String() + } + if descriptor.MediaType != "" && descriptor.Digest.Validate() == nil && descriptor.Size > 0 { + metadata[docker.ExporterImageDescriptorKey] = descriptor + } + if descriptor.Digest.Validate() == nil { + metadata[docker.ExporterImageDigestKey] = descriptor.Digest + } + return metadata, nil +} diff --git a/vendor/github.com/containers/buildah/internal/mkcw/attest.go b/vendor/github.com/containers/buildah/internal/mkcw/attest.go index b974ca5242..d93bcc17a8 100644 --- a/vendor/github.com/containers/buildah/internal/mkcw/attest.go +++ b/vendor/github.com/containers/buildah/internal/mkcw/attest.go @@ -14,7 +14,7 @@ import ( "path/filepath" "strings" - "github.com/containers/buildah/internal/mkcw/types" + types "github.com/containers/buildah/internal/mkcw/types" "github.com/sirupsen/logrus" "go.podman.io/storage/pkg/fileutils" ) diff --git a/vendor/github.com/containers/buildah/internal/mkcw/embed/entrypoint_amd64.gz b/vendor/github.com/containers/buildah/internal/mkcw/embed/entrypoint_amd64.gz index 953670818f..c2399f9e90 100644 Binary files a/vendor/github.com/containers/buildah/internal/mkcw/embed/entrypoint_amd64.gz and b/vendor/github.com/containers/buildah/internal/mkcw/embed/entrypoint_amd64.gz differ diff --git a/vendor/github.com/containers/buildah/internal/mkcw/types/attest.go b/vendor/github.com/containers/buildah/internal/mkcw/types/attest.go index f92f72cb97..a09a40848c 100644 --- a/vendor/github.com/containers/buildah/internal/mkcw/types/attest.go +++ b/vendor/github.com/containers/buildah/internal/mkcw/types/attest.go @@ -1,4 +1,4 @@ -package types +package mkcwtypes // RegistrationRequest is the body of the request which we use for registering // this confidential workload with the attestation server. diff --git a/vendor/github.com/containers/buildah/internal/mkcw/types/workload.go b/vendor/github.com/containers/buildah/internal/mkcw/types/workload.go index a0e4f5254c..4f6d658c7e 100644 --- a/vendor/github.com/containers/buildah/internal/mkcw/types/workload.go +++ b/vendor/github.com/containers/buildah/internal/mkcw/types/workload.go @@ -1,4 +1,4 @@ -package types +package mkcwtypes import "github.com/containers/buildah/define" diff --git a/vendor/github.com/containers/buildah/internal/mkcw/workload.go b/vendor/github.com/containers/buildah/internal/mkcw/workload.go index e407b22f93..fe8ea248b6 100644 --- a/vendor/github.com/containers/buildah/internal/mkcw/workload.go +++ b/vendor/github.com/containers/buildah/internal/mkcw/workload.go @@ -10,7 +10,7 @@ import ( "os" "github.com/containers/buildah/define" - "github.com/containers/buildah/internal/mkcw/types" + types "github.com/containers/buildah/internal/mkcw/types" ) type ( diff --git a/vendor/github.com/containers/buildah/internal/sanitize/sanitize.go b/vendor/github.com/containers/buildah/internal/sanitize/sanitize.go new file mode 100644 index 0000000000..c9904e3107 --- /dev/null +++ b/vendor/github.com/containers/buildah/internal/sanitize/sanitize.go @@ -0,0 +1,340 @@ +package sanitize + +import ( + "archive/tar" + "errors" + "fmt" + "io" + "os" + "path" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + directoryTransport "go.podman.io/image/v5/directory" + dockerTransport "go.podman.io/image/v5/docker" + dockerArchiveTransport "go.podman.io/image/v5/docker/archive" + ociArchiveTransport "go.podman.io/image/v5/oci/archive" + ociLayoutTransport "go.podman.io/image/v5/oci/layout" + openshiftTransport "go.podman.io/image/v5/openshift" + "go.podman.io/image/v5/pkg/compression" + "go.podman.io/storage/pkg/archive" + "go.podman.io/storage/pkg/chrootarchive" +) + +// create a temporary file to use as a destination archive +func newArchiveDestination(tmpdir string) (tw *tar.Writer, f *os.File, err error) { + if f, err = os.CreateTemp(tmpdir, "buildah-archive-"); err != nil { + return nil, nil, fmt.Errorf("creating temporary copy of base image: %w", err) + } + tw = tar.NewWriter(f) + return tw, f, nil +} + +// create a temporary directory to use as a new OCI layout or "image in a directory" image +func newDirectoryDestination(tmpdir string) (string, error) { + newDirectory, err := os.MkdirTemp(tmpdir, "buildah-layout-") + if err != nil { + return "", fmt.Errorf("creating temporary copy of base image: %w", err) + } + return newDirectory, nil +} + +// create an archive containing a single item from the build context +func newSingleItemArchive(contextDir, archiveSource string) (io.ReadCloser, error) { + for { + // try to make sure the archiver doesn't get thrown by relative prefixes + if strings.HasPrefix(archiveSource, "/") && archiveSource != "/" { + archiveSource = strings.TrimPrefix(archiveSource, "/") + continue + } else if strings.HasPrefix(archiveSource, "./") && archiveSource != "./" { + archiveSource = strings.TrimPrefix(archiveSource, "./") + continue + } + break + } + // grab only that one file, ignore anything and everything else + tarOptions := &archive.TarOptions{ + IncludeFiles: []string{path.Clean(archiveSource)}, + ExcludePatterns: []string{"**"}, + } + return chrootarchive.Tar(contextDir, tarOptions, contextDir) +} + +// Write this header/content combination to a tar writer, making sure that it +// doesn't include any symbolic links that point to something which hasn't +// already been seen in this archive. Overwrites the contents of `*hdr`. +func writeToArchive(tw *tar.Writer, hdr *tar.Header, content io.Reader, seenEntries map[string]struct{}, convertSymlinksToHardlinks bool) error { + // write to the archive writer + hdr.Name = path.Clean("/" + hdr.Name) + if hdr.Name != "/" { + hdr.Name = strings.TrimPrefix(hdr.Name, "/") + } + seenEntries[hdr.Name] = struct{}{} + switch hdr.Typeflag { + case tar.TypeDir, tar.TypeReg, tar.TypeLink: + // all good + case tar.TypeSymlink: + // resolve the target of the symlink + linkname := hdr.Linkname + if !path.IsAbs(linkname) { + linkname = path.Join("/"+path.Dir(hdr.Name), linkname) + } + linkname = path.Clean(linkname) + if linkname != "/" { + linkname = strings.TrimPrefix(linkname, "/") + } + if _, validTarget := seenEntries[linkname]; !validTarget { + return fmt.Errorf("invalid symbolic link from %q to %q (%q) in archive", hdr.Name, hdr.Linkname, linkname) + } + rel, err := filepath.Rel(filepath.FromSlash(path.Dir("/"+hdr.Name)), filepath.FromSlash("/"+linkname)) + if err != nil { + return fmt.Errorf("computing relative path from %q to %q in archive", hdr.Name, linkname) + } + rel = filepath.ToSlash(rel) + if convertSymlinksToHardlinks { + // rewrite as a hard link for oci-archive, which gets + // extracted into a temporary directory to be read, but + // not for docker-archive, which is read directly from + // the unextracted archive file, in a way which doesn't + // understand hard links + hdr.Typeflag = tar.TypeLink + hdr.Linkname = linkname + } else { + // ensure it's a relative symlink inside of the tree + // for docker-archive + hdr.Linkname = rel + } + default: + return fmt.Errorf("rewriting archive of base image: disallowed entry type %c", hdr.Typeflag) + } + if err := tw.WriteHeader(hdr); err != nil { + return fmt.Errorf("rewriting archive of base image: %w", err) + } + if hdr.Typeflag == tar.TypeReg { + n, err := io.Copy(tw, content) + if err != nil { + return fmt.Errorf("copying content for %q in base image: %w", hdr.Name, err) + } + if n != hdr.Size { + return fmt.Errorf("short write writing %q in base image: %d != %d", hdr.Name, n, hdr.Size) + } + } + return nil +} + +// write this header and possible content to a directory tree +func writeToDirectory(root string, hdr *tar.Header, content io.Reader) error { + var err error + // write this item directly to a directory tree. the reader won't care + // much about permissions or datestamps, so don't bother setting them + hdr.Name = path.Clean("/" + hdr.Name) + newName := filepath.Join(root, filepath.FromSlash(hdr.Name)) + switch hdr.Typeflag { + case tar.TypeDir: + err = os.Mkdir(newName, 0o700) + case tar.TypeReg: + err = func() error { + var f *os.File + f, err := os.OpenFile(newName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) + if err != nil { + return fmt.Errorf("copying content for %q in base image: %w", hdr.Name, err) + } + n, err := io.Copy(f, content) + if err != nil { + f.Close() + return fmt.Errorf("copying content for %q in base image: %w", hdr.Name, err) + } + if n != hdr.Size { + f.Close() + return fmt.Errorf("short write writing %q in base image: %d != %d", hdr.Name, n, hdr.Size) + } + return f.Close() + }() + case tar.TypeLink: + linkName := path.Clean("/" + hdr.Linkname) + oldName := filepath.Join(root, filepath.FromSlash(linkName)) + err = os.Link(oldName, newName) + case tar.TypeSymlink: // convert it to a hard link or absolute symlink + var oldName string + if !path.IsAbs(path.Clean("/" + hdr.Linkname)) { + linkName := path.Join("/"+path.Dir(hdr.Name), hdr.Linkname) + oldName = filepath.Join(root, filepath.FromSlash(linkName)) + } else { + oldName = filepath.Join(root, filepath.FromSlash(path.Clean("/"+hdr.Linkname))) + } + err = os.Link(oldName, newName) + if err != nil && errors.Is(err, os.ErrPermission) { // EPERM could mean it's a directory + if oldInfo, err2 := os.Stat(oldName); err2 == nil && oldInfo.IsDir() { + err = os.Symlink(oldName, newName) + } + } + default: + return fmt.Errorf("extracting archive of base image: disallowed entry type %c", hdr.Typeflag) + } + if err != nil { + return fmt.Errorf("creating %q: %w", newName, err) + } + return nil +} + +// ImageName limits which image transports we'll accept. For those it accepts +// which refer to filesystem objects, where relative path names are evaluated +// relative to "contextDir", it will create a copy of the original image, under +// "tmpdir", which contains no symbolic links. It it returns a parseable +// reference to the image which should be used. +func ImageName(transportName, restOfImageName, contextDir, tmpdir string) (newFrom string, err error) { + seenEntries := make(map[string]struct{}) + // we're going to try to create a temporary directory or file, but if + // we fail, make sure that they get removed immediately + newImageDestination := "" + succeeded := false + defer func() { + if !succeeded && newImageDestination != "" { + if err := os.RemoveAll(newImageDestination); err != nil { + logrus.Warnf("removing temporary copy of base image in %q: %v", newImageDestination, err) + } + } + }() + + // create an archive of the base image, whatever kind it is, chrooting into + // the build context directory while doing so, to be sure that it can't + // be tricked into including anything from outside of the context directory + isEmbeddedArchive := false + var f *os.File + var tw *tar.Writer + var archiveSource string + var imageArchive io.ReadCloser + switch transportName { + case dockerTransport.Transport.Name(), "docker-daemon", openshiftTransport.Transport.Name(): // ok, these are all remote + return transportName + ":" + restOfImageName, nil + case dockerArchiveTransport.Transport.Name(), ociArchiveTransport.Transport.Name(): // these are, basically, tarballs + // these take the form path[:stuff] + transportRef := restOfImageName + archiveSource, refLeftover, ok := strings.Cut(transportRef, ":") + if ok { + refLeftover = ":" + refLeftover + } + // create a temporary file to use as our new archive + tw, f, err = newArchiveDestination(tmpdir) + if err != nil { + return "", fmt.Errorf("creating temporary copy of base image: %w", err) + } + newImageDestination = f.Name() + defer func() { + if tw != nil { + if err := tw.Close(); err != nil { + logrus.Warnf("wrapping up writing copy of base image to archive %q: %v", newImageDestination, err) + } + } + if f != nil { + if err := f.Close(); err != nil { + logrus.Warnf("closing copy of base image in archive file %q: %v", newImageDestination, err) + } + } + }() + // archive only the archive file for copying to the new archive file + imageArchive, err = newSingleItemArchive(contextDir, archiveSource) + isEmbeddedArchive = true + // generate the new reference using the temporary file's name + newFrom = transportName + ":" + newImageDestination + refLeftover + case ociLayoutTransport.Transport.Name(): // this is a directory tree + // this takes the form path[:stuff] + transportRef := restOfImageName + archiveSource, refLeftover, ok := strings.Cut(transportRef, ":") + if ok { + refLeftover = ":" + refLeftover + } + // create a new directory to use as our new layout directory + if newImageDestination, err = newDirectoryDestination(tmpdir); err != nil { + return "", fmt.Errorf("creating temporary copy of base image: %w", err) + } + // archive the entire layout directory for copying to the new layout directory + tarOptions := &archive.TarOptions{} + imageArchive, err = chrootarchive.Tar(filepath.Join(contextDir, archiveSource), tarOptions, contextDir) + // generate the new reference using the directory + newFrom = transportName + ":" + newImageDestination + refLeftover + case directoryTransport.Transport.Name(): // this is also a directory tree + // this takes the form of just a path + transportRef := restOfImageName + // create a new directory to use as our new image directory + if newImageDestination, err = newDirectoryDestination(tmpdir); err != nil { + return "", fmt.Errorf("creating temporary copy of base image: %w", err) + } + // archive the entire directory for copying to the new directory + archiveSource = transportRef + tarOptions := &archive.TarOptions{} + imageArchive, err = chrootarchive.Tar(filepath.Join(contextDir, archiveSource), tarOptions, contextDir) + // generate the new reference using the directory + newFrom = transportName + ":" + newImageDestination + default: + return "", fmt.Errorf("unexpected container image transport %q", transportName) + } + if err != nil { + return "", fmt.Errorf("error archiving source at %q under %q", archiveSource, contextDir) + } + + // start reading the archived content + defer func() { + if err := imageArchive.Close(); err != nil { + logrus.Warn(err) + } + }() + tr := tar.NewReader(imageArchive) + hdr, err := tr.Next() + for err == nil { + // if the archive we're parsing is expected to have an archive as its only item, + // assume it's the first (and hopefully, only) item, and switch to stepping through + // it as the archive + if isEmbeddedArchive { + if hdr.Typeflag != tar.TypeReg { + return "", fmt.Errorf("internal error passing archive contents: embedded archive type was %c instead of %c", hdr.Typeflag, tar.TypeReg) + } + decompressed, _, decompressErr := compression.AutoDecompress(tr) + if decompressErr != nil { + return "", fmt.Errorf("error decompressing-if-necessary archive %q: %w", archiveSource, decompressErr) + } + defer func() { + if err := decompressed.Close(); err != nil { + logrus.Warn(err) + } + }() + tr = tar.NewReader(decompressed) + hdr, err = tr.Next() + isEmbeddedArchive = false + continue + } + // write this item from the source archive to either the new archive or the new + // directory, which ever one we're doing + var writeError error + if tw != nil { + writeError = writeToArchive(tw, hdr, io.LimitReader(tr, hdr.Size), seenEntries, transportName == ociArchiveTransport.Transport.Name()) + } else { + writeError = writeToDirectory(newImageDestination, hdr, io.LimitReader(tr, hdr.Size)) + } + if writeError != nil { + return "", fmt.Errorf("writing copy of image to %q: %w", newImageDestination, writeError) + } + hdr, err = tr.Next() + } + if err != nil && !errors.Is(err, io.EOF) { + return "", fmt.Errorf("reading archive of base image: %w", err) + } + if isEmbeddedArchive { + logrus.Warnf("expected to have archived a copy of %q, missed it", archiveSource) + } + if tw != nil { + if err := tw.Close(); err != nil { + return "", fmt.Errorf("wrapping up writing copy of base image to archive %q: %w", newImageDestination, err) + } + tw = nil + } + if f != nil { + if err := f.Close(); err != nil { + return "", fmt.Errorf("closing copy of base image in archive file %q: %w", newImageDestination, err) + } + f = nil + } + succeeded = true + return newFrom, nil +} diff --git a/vendor/github.com/containers/buildah/internal/types.go b/vendor/github.com/containers/buildah/internal/types.go index 47061c4a37..a1e8078e66 100644 --- a/vendor/github.com/containers/buildah/internal/types.go +++ b/vendor/github.com/containers/buildah/internal/types.go @@ -22,4 +22,5 @@ type StageMountDetails struct { IsImage bool // true if the mountpoint is an image's rootfs IsAdditionalBuildContext bool // true if the mountpoint is an additional build context MountPoint string // mountpoint of the stage or image's root directory or path of the additional build context + IsWritesDiscardedOverlay bool // this is an overlay that discards writes } diff --git a/vendor/github.com/containers/buildah/internal/util/util.go b/vendor/github.com/containers/buildah/internal/util/util.go index b230d0e305..9094a7023c 100644 --- a/vendor/github.com/containers/buildah/internal/util/util.go +++ b/vendor/github.com/containers/buildah/internal/util/util.go @@ -1,4 +1,4 @@ -package util +package internalutil import ( "fmt" diff --git a/vendor/github.com/containers/buildah/internal/volumes/volumes.go b/vendor/github.com/containers/buildah/internal/volumes/volumes.go index 8b4b30cd79..2826fec4b1 100644 --- a/vendor/github.com/containers/buildah/internal/volumes/volumes.go +++ b/vendor/github.com/containers/buildah/internal/volumes/volumes.go @@ -141,6 +141,7 @@ func GetBindMount(sys *types.SystemContext, args []string, contextDir string, st setDest := "" bindNonRecursive := false fromWhere := "" + skipOverlay := false for _, val := range args { argName, argValue, hasArgValue := strings.Cut(val, "=") @@ -248,6 +249,7 @@ func GetBindMount(sys *types.SystemContext, args []string, contextDir string, st if additionalMountPoints != nil { if val, ok := additionalMountPoints[fromWhere]; ok { mountPoint = val.MountPoint + skipOverlay = val.IsWritesDiscardedOverlay } } // if mountPoint of image was not found in additionalMap @@ -273,6 +275,14 @@ func GetBindMount(sys *types.SystemContext, args []string, contextDir string, st }() } contextDir = mountPoint + } else { + // special case an additional mount point for "" as shorthand for "preferred location of the default build context" + if additionalMountPoints != nil { + if val, ok := additionalMountPoints[""]; ok { + contextDir = val.MountPoint + skipOverlay = val.IsWritesDiscardedOverlay + } + } } // buildkit parity: default bind option must be `rbind` @@ -328,7 +338,7 @@ func GetBindMount(sys *types.SystemContext, args []string, contextDir string, st } overlayDir := "" - if mountedImage != "" || mountIsReadWrite(newMount) { + if !skipOverlay && (mountedImage != "" || mountIsReadWrite(newMount)) { if newMount, overlayDir, err = convertToOverlay(newMount, store, mountLabel, tmpDir, 0, 0); err != nil { return newMount, "", "", "", err } diff --git a/vendor/github.com/containers/buildah/pkg/cli/build.go b/vendor/github.com/containers/buildah/pkg/cli/build.go index 9449ac883a..2b7c2ff7c4 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/build.go +++ b/vendor/github.com/containers/buildah/pkg/cli/build.go @@ -112,7 +112,10 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( if c.Flag("build-context").Changed { for _, contextString := range iopts.BuildContext { av := strings.SplitN(contextString, "=", 2) - if len(av) > 1 { + // the key should be non-empty: we use "" as internal + // shorthand for the default build context when there's + // an overlay mounted over it + if len(av) > 1 && av[0] != "" { parseAdditionalBuildContext, err := parse.GetAdditionalBuildContext(av[1]) if err != nil { return options, nil, nil, fmt.Errorf("while parsing additional build context: %w", err) @@ -405,6 +408,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( GroupAdd: iopts.GroupAdd, IDMappingOptions: idmappingOptions, IIDFile: iopts.Iidfile, + IIDFileRaw: iopts.IidfileRaw, IgnoreFile: iopts.IgnoreFile, In: stdin, InheritLabels: inheritLabels, @@ -418,6 +422,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( LogRusage: iopts.LogRusage, LogSplitByPlatform: iopts.LogSplitByPlatform, Manifest: iopts.Manifest, + MetadataFile: iopts.MetadataFile, MaxPullPushRetries: iopts.Retry, NamespaceOptions: namespaceOptions, NoCache: iopts.NoCache, diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index ebeb51e1f7..6d68f42b4e 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -73,6 +73,7 @@ type BudResults struct { Format string From string Iidfile string + IidfileRaw string InheritLabels bool InheritAnnotations bool Label []string @@ -80,6 +81,7 @@ type BudResults struct { Logfile string LogSplitByPlatform bool Manifest string + MetadataFile string NoHostname bool NoHosts bool NoCache bool @@ -252,6 +254,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { fs.StringSliceVarP(&flags.File, "file", "f", []string{}, "`pathname or URL` of a Dockerfile") fs.StringVar(&flags.Format, "format", DefaultFormat(), "`format` of the built image's manifest and metadata. Use BUILDAH_FORMAT environment variable to override.") fs.StringVar(&flags.Iidfile, "iidfile", "", "`file` to write the image ID to") + fs.StringVar(&flags.IidfileRaw, "iidfile-raw", "", "`file` to write the image ID to (without algorithm prefix)") fs.IntVar(&flags.Jobs, "jobs", 1, "how many stages to run in parallel") fs.StringArrayVar(&flags.Label, "label", []string{}, "set metadata for an image (default [])") fs.StringArrayVar(&flags.LayerLabel, "layer-label", []string{}, "set metadata for an intermediate image (default [])") @@ -270,6 +273,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { panic(fmt.Sprintf("error marking the rusage-logfile flag as hidden: %v", err)) } fs.StringVar(&flags.Manifest, "manifest", "", "add the image to the specified manifest list. Creates manifest list if it does not exist") + fs.StringVar(&flags.MetadataFile, "metadata-file", "", "`file` to write metadata about the image to") fs.BoolVar(&flags.NoCache, "no-cache", false, "do not use existing cached images for the container build. Build from the start with a new set of cached layers.") fs.BoolVar(&flags.NoHostname, "no-hostname", false, "do not create new /etc/hostname file for RUN instructions, use the one from the base image.") fs.BoolVar(&flags.NoHosts, "no-hosts", false, "do not create new /etc/hosts file for RUN instructions, use the one from the base image.") @@ -355,11 +359,13 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["hooks-dir"] = commonComp.AutocompleteNone flagCompletion["ignorefile"] = commonComp.AutocompleteDefault flagCompletion["iidfile"] = commonComp.AutocompleteDefault + flagCompletion["iidfile-raw"] = commonComp.AutocompleteDefault flagCompletion["jobs"] = commonComp.AutocompleteNone flagCompletion["label"] = commonComp.AutocompleteNone flagCompletion["layer-label"] = commonComp.AutocompleteNone flagCompletion["logfile"] = commonComp.AutocompleteDefault flagCompletion["manifest"] = commonComp.AutocompleteDefault + flagCompletion["metadata-file"] = commonComp.AutocompleteDefault flagCompletion["os"] = commonComp.AutocompleteNone flagCompletion["os-feature"] = commonComp.AutocompleteNone flagCompletion["os-version"] = commonComp.AutocompleteNone @@ -542,6 +548,8 @@ func AliasFlags(_ *pflag.FlagSet, name string) pflag.NormalizedName { name = "os" case "purge": name = "rm" + case "raw-iidfile": + name = "iidfile-raw" case "tty": name = "terminal" } diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go index 5607a0b163..4353c7bbaf 100644 --- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go +++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go @@ -19,10 +19,10 @@ import ( // Options for MountWithOptions(). type Options struct { - // The Upper directory is normally writable layer in an overlay mount. - // Note!! : Following API does not handles escaping or validates correctness of the values - // passed to UpperDirOptionFragment instead API will try to pass values as is it - // to the `mount` command. It is user's responsibility to make sure they pre-validate + // The Upper directory is normally the writable layer in an overlay mount. + // Note!! : Following API does not handle escaping or validate the correctness of the values + // passed to UpperDirOptionFragment; instead the API will try to pass values as-given + // to the `mount` command. It is the caller's responsibility to make sure they pre-validate // these values. Invalid inputs may lead to undefined behaviour. // This is provided as-is, use it if it works for you, we can/will change/break that in the future. // See discussion here for more context: https://github.com/containers/buildah/pull/3715#discussion_r786036959 @@ -30,9 +30,9 @@ type Options struct { // `comma`, `backslash` ,`colon` and any other special characters UpperDirOptionFragment string // The Workdir is used to prepare files as they are switched between the layers. - // Note!! : Following API does not handles escaping or validates correctness of the values - // passed to WorkDirOptionFragment instead API will try to pass values as is it - // to the `mount` command. It is user's responsibility to make sure they pre-validate + // Note!! : Following API does not handle escaping or validate the correctness of the values + // passed to WorkDirOptionFragment; instead the API will try to pass values as-given + // to the `mount` command. It is the caller's responsibility to make sure they pre-validate // these values. Invalid inputs may lead to undefined behaviour. // This is provided as-is, use it if it works for you, we can/will change/break that in the future. // See discussion here for more context: https://github.com/containers/buildah/pull/3715#discussion_r786036959 diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go index 8f59414746..512692f693 100644 --- a/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go +++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go @@ -1,9 +1,11 @@ package overlay import ( + "errors" "fmt" "os" "path/filepath" + "strconv" "strings" "syscall" @@ -50,19 +52,41 @@ func MountWithOptions(contentDir, source, dest string, opts *Options) (mount spe if !filepath.IsAbs(upperDir) { upperDir = filepath.Join(contentDir, upperDir) } - } - - st, err := os.Stat(source) - if err != nil { - return mount, err - } - if err := os.Chmod(upperDir, st.Mode()); err != nil { - return mount, err - } - if stat, ok := st.Sys().(*syscall.Stat_t); ok { - if err := os.Chown(upperDir, int(stat.Uid), int(stat.Gid)); err != nil { + } else { + st, err := os.Stat(source) + if err != nil { return mount, err } + if err := os.Chmod(upperDir, st.Mode()); err != nil { + return mount, err + } + if stat, ok := st.Sys().(*syscall.Stat_t); ok { + if err := os.Chown(upperDir, int(stat.Uid), int(stat.Gid)); err != nil { + if !errors.Is(err, syscall.EINVAL) { + return mount, err + } + overflowed := false + overflowUIDText, uerr := os.ReadFile("/proc/sys/kernel/overflowuid") + overflowGIDText, gerr := os.ReadFile("/proc/sys/kernel/overflowgid") + if uerr == nil && gerr == nil { + overflowUID, uerr := strconv.Atoi(strings.TrimSpace(string(overflowUIDText))) + overflowGID, gerr := strconv.Atoi(strings.TrimSpace(string(overflowGIDText))) + if uerr == nil && gerr == nil && int(stat.Uid) == overflowUID && int(stat.Gid) == overflowGID { + overflowed = true + } + } + if !overflowed { + return mount, err + } + } + times := []syscall.Timespec{ + stat.Atim, + stat.Mtim, + } + if err := syscall.UtimesNano(upperDir, times); err != nil { + return mount, err + } + } } overlayOptions = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s,private", escapeColon(source), upperDir, workDir) } diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index 911d5dedac..aa2a9672b2 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -60,7 +60,12 @@ const ( BuildahCacheDir = "buildah-cache" ) -var errInvalidSecretSyntax = errors.New("incorrect secret flag format: should be --secret id=foo,src=bar[,env=ENV][,type=file|env]") +var ( + errInvalidSecretSyntax = errors.New("incorrect secret flag format: should be --secret id=foo,src=bar[,env=ENV][,type=file|env]") + errInvalidBuildContextPathname = errors.New(`invalid build context path ""`) + errInvalidBuildContextImage = errors.New(`invalid build context image name ""`) + errInvalidBuildContextURL = errors.New(`invalid build context image URL ""`) +) // RepoNamesToNamedReferences parse the raw string to Named reference func RepoNamesToNamedReferences(destList []string) ([]reference.Named, error) { @@ -244,21 +249,39 @@ func CommonBuildOptionsFromFlagSet(flags *pflag.FlagSet, findFlagFunc func(name return commonOpts, nil } -// GetAdditionalBuildContext consumes raw string and returns parsed AdditionalBuildContext +// GetAdditionalBuildContext consumes a raw string and returns a parsed +// AdditionalBuildContext describing the build context. func GetAdditionalBuildContext(value string) (define.AdditionalBuildContext, error) { + if value == "" { + // reject empty values (filesystem paths?), because elsewhere we use an + // empty string as an internal nickname for the default build context + return define.AdditionalBuildContext{}, errInvalidBuildContextPathname + } ret := define.AdditionalBuildContext{IsURL: false, IsImage: false, Value: value} if strings.HasPrefix(value, "docker-image://") { ret.IsImage = true ret.Value = strings.TrimPrefix(value, "docker-image://") + if ret.Value == "" { + return define.AdditionalBuildContext{}, errInvalidBuildContextImage + } } else if strings.HasPrefix(value, "container-image://") { ret.IsImage = true ret.Value = strings.TrimPrefix(value, "container-image://") + if ret.Value == "" { + return define.AdditionalBuildContext{}, errInvalidBuildContextImage + } } else if strings.HasPrefix(value, "docker://") { ret.IsImage = true ret.Value = strings.TrimPrefix(value, "docker://") + if ret.Value == "" { + return define.AdditionalBuildContext{}, errInvalidBuildContextImage + } } else if strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") { ret.IsImage = false ret.IsURL = true + if strings.TrimPrefix(ret.Value, "http://") == "" || strings.TrimPrefix(ret.Value, "https://") == "" { + return define.AdditionalBuildContext{}, errInvalidBuildContextURL + } } else { path, err := filepath.Abs(value) if err != nil { diff --git a/vendor/github.com/containers/buildah/pkg/util/resource_unix.go b/vendor/github.com/containers/buildah/pkg/util/resource_unix.go index cc62f860cd..725b58438a 100644 --- a/vendor/github.com/containers/buildah/pkg/util/resource_unix.go +++ b/vendor/github.com/containers/buildah/pkg/util/resource_unix.go @@ -1,6 +1,6 @@ //go:build linux || freebsd || darwin -package util +package util //nolint:revive,nolintlint import ( "fmt" diff --git a/vendor/github.com/containers/buildah/pkg/util/resource_windows.go b/vendor/github.com/containers/buildah/pkg/util/resource_windows.go index 371709186e..1c50ce3785 100644 --- a/vendor/github.com/containers/buildah/pkg/util/resource_windows.go +++ b/vendor/github.com/containers/buildah/pkg/util/resource_windows.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "fmt" diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go b/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go index d185cb45f1..ae018a80ca 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "errors" diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go b/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go index 7112aba38e..b3b240aa5e 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "time" diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go index 163e5762d5..dbe35a932a 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "bytes" diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_netbsd.go b/vendor/github.com/containers/buildah/pkg/util/uptime_netbsd.go index fc981ac0d5..0ccbe69f61 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_netbsd.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_netbsd.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "time" diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go b/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go index ef3adac2a1..4e30fbf3e1 100644 --- a/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "errors" diff --git a/vendor/github.com/containers/buildah/pkg/util/util.go b/vendor/github.com/containers/buildah/pkg/util/util.go index 17ad36056a..6f2693caf1 100644 --- a/vendor/github.com/containers/buildah/pkg/util/util.go +++ b/vendor/github.com/containers/buildah/pkg/util/util.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "fmt" diff --git a/vendor/github.com/containers/buildah/pkg/util/version_unix.go b/vendor/github.com/containers/buildah/pkg/util/version_unix.go index c8b7002a38..770ce669c7 100644 --- a/vendor/github.com/containers/buildah/pkg/util/version_unix.go +++ b/vendor/github.com/containers/buildah/pkg/util/version_unix.go @@ -1,6 +1,6 @@ //go:build !windows -package util +package util //nolint:revive,nolintlint import ( "bytes" diff --git a/vendor/github.com/containers/buildah/pkg/util/version_windows.go b/vendor/github.com/containers/buildah/pkg/util/version_windows.go index 35a1e34804..474f362061 100644 --- a/vendor/github.com/containers/buildah/pkg/util/version_windows.go +++ b/vendor/github.com/containers/buildah/pkg/util/version_windows.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "errors" diff --git a/vendor/github.com/containers/buildah/run.go b/vendor/github.com/containers/buildah/run.go index a5e3a00ad3..4f20f9c1b8 100644 --- a/vendor/github.com/containers/buildah/run.go +++ b/vendor/github.com/containers/buildah/run.go @@ -168,14 +168,16 @@ type RunOptions struct { SSHSources map[string]*sshagent.Source `json:"-"` // RunMounts are unparsed mounts to be added for this run RunMounts []string - // Map of stages and container mountpoint if any from stage executor + // Map of already-mounted stages, images, and container mountpoints + // which entries in `RunMounts` might be referring to. If a value for + // the "" key is present, it points to the context directory. StageMountPoints map[string]internal.StageMountDetails // IDs of mounted images to be unmounted before returning // Deprecated: before 1.39, these images would not be consistently // unmounted if Run() returned an error ExternalImageMounts []string // System context of current build - SystemContext *types.SystemContext + SystemContext *types.SystemContext `json:"-"` // CgroupManager to use for running OCI containers CgroupManager string // CDIConfigDir is the location of CDI configuration files, if the files in diff --git a/vendor/github.com/containers/buildah/util/types.go b/vendor/github.com/containers/buildah/util/types.go index 91c9ace14a..c8ed3e5a26 100644 --- a/vendor/github.com/containers/buildah/util/types.go +++ b/vendor/github.com/containers/buildah/util/types.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "github.com/containers/buildah/define" diff --git a/vendor/github.com/containers/buildah/util/util.go b/vendor/github.com/containers/buildah/util/util.go index 0b2339e16d..a47aab8f48 100644 --- a/vendor/github.com/containers/buildah/util/util.go +++ b/vendor/github.com/containers/buildah/util/util.go @@ -1,4 +1,4 @@ -package util +package util //nolint:revive,nolintlint import ( "errors" diff --git a/vendor/github.com/containers/buildah/util/util_unix.go b/vendor/github.com/containers/buildah/util/util_unix.go index 507e4892c6..05200f20f8 100644 --- a/vendor/github.com/containers/buildah/util/util_unix.go +++ b/vendor/github.com/containers/buildah/util/util_unix.go @@ -1,6 +1,6 @@ //go:build linux || darwin || freebsd || netbsd -package util +package util //nolint:revive,nolintlint import ( "os" diff --git a/vendor/github.com/containers/buildah/util/util_unsupported.go b/vendor/github.com/containers/buildah/util/util_unsupported.go index 36d0664dda..2093a6c93b 100644 --- a/vendor/github.com/containers/buildah/util/util_unsupported.go +++ b/vendor/github.com/containers/buildah/util/util_unsupported.go @@ -1,6 +1,6 @@ //go:build !linux -package util +package util //nolint:revive,nolintlint // IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode. func IsCgroup2UnifiedMode() (bool, error) { diff --git a/vendor/github.com/containers/buildah/util/util_windows.go b/vendor/github.com/containers/buildah/util/util_windows.go index 0a8fa59465..00478b1cb3 100644 --- a/vendor/github.com/containers/buildah/util/util_windows.go +++ b/vendor/github.com/containers/buildah/util/util_windows.go @@ -1,6 +1,6 @@ //go:build !linux && !darwin -package util +package util //nolint:revive,nolintlint import ( "os" diff --git a/vendor/github.com/fsouza/go-dockerclient/misc.go b/vendor/github.com/fsouza/go-dockerclient/misc.go index 2fa19c67f4..827cd29dfc 100644 --- a/vendor/github.com/fsouza/go-dockerclient/misc.go +++ b/vendor/github.com/fsouza/go-dockerclient/misc.go @@ -144,7 +144,7 @@ func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) { *ipnet = NetIPNet(*cidr) } } - return + return err } // IndexInfo contains information about a registry. diff --git a/vendor/github.com/moby/buildkit/util/stack/stack.pb.go b/vendor/github.com/moby/buildkit/util/stack/stack.pb.go index 5e9ef1894e..583565672b 100644 --- a/vendor/github.com/moby/buildkit/util/stack/stack.pb.go +++ b/vendor/github.com/moby/buildkit/util/stack/stack.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.9 +// protoc-gen-go v1.36.10 // protoc v3.11.4 // source: github.com/moby/buildkit/util/stack/stack.proto diff --git a/vendor/go.podman.io/image/v5/copy/copy.go b/vendor/go.podman.io/image/v5/copy/copy.go index eed5f8d96d..cc165ff711 100644 --- a/vendor/go.podman.io/image/v5/copy/copy.go +++ b/vendor/go.podman.io/image/v5/copy/copy.go @@ -14,6 +14,7 @@ import ( "github.com/sirupsen/logrus" "go.podman.io/image/v5/docker/reference" internalblobinfocache "go.podman.io/image/v5/internal/blobinfocache" + "go.podman.io/image/v5/internal/digests" "go.podman.io/image/v5/internal/image" "go.podman.io/image/v5/internal/imagedestination" "go.podman.io/image/v5/internal/imagesource" @@ -155,6 +156,15 @@ type Options struct { // In oci-archive: destinations, this will set the create/mod/access timestamps in each tar entry // (but not a timestamp of the created archive file). DestinationTimestamp *time.Time + + // FIXME: + // - this reference to an internal type is unusable from the outside even if we made the field public + // - what is the actual semantics? Right now it is probably “choices to use when writing to the destination”, TBD + // - anyway do we want to expose _all_ of the digests.Options tunables, or fewer? + // - … do we want to expose _more_ granularity than that? + // - (“must have at least sha512 integrity when reading”, what does “at least” mean for random pairs of algorithms?) + // - should some of this be in config files, maybe ever per-registry? + digestOptions digests.Options } // OptionCompressionVariant allows to supply information about @@ -200,6 +210,12 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, if options == nil { options = &Options{} } + // FIXME: Currently, digestsOptions is not implemented at all, and exists in the codebase + // only to allow gradually building the feature set. + // After c/image/copy consistently implements it, provide a public digest options API of some kind. + optionsCopy := *options + optionsCopy.digestOptions = digests.CanonicalDefault() + options = &optionsCopy if err := validateImageListSelection(options.ImageListSelection); err != nil { return nil, err diff --git a/vendor/go.podman.io/image/v5/copy/single.go b/vendor/go.podman.io/image/v5/copy/single.go index 5a5d5e3ddb..2280e7f53a 100644 --- a/vendor/go.podman.io/image/v5/copy/single.go +++ b/vendor/go.podman.io/image/v5/copy/single.go @@ -381,14 +381,14 @@ func (ic *imageCopier) noPendingManifestUpdates() bool { func (ic *imageCopier) compareImageDestinationManifestEqual(ctx context.Context, targetInstance *digest.Digest) (*copySingleImageResult, error) { destImageSource, err := ic.c.dest.Reference().NewImageSource(ctx, ic.c.options.DestinationCtx) if err != nil { - logrus.Debugf("Unable to create destination image %s source: %v", ic.c.dest.Reference(), err) + logrus.Debugf("Unable to create destination image %s source: %v", transports.ImageName(ic.c.dest.Reference()), err) return nil, nil } defer destImageSource.Close() destManifest, destManifestType, err := destImageSource.GetManifest(ctx, targetInstance) if err != nil { - logrus.Debugf("Unable to get destination image %s/%s manifest: %v", destImageSource, targetInstance, err) + logrus.Debugf("Unable to get destination image %s/%s manifest: %v", transports.ImageName(destImageSource.Reference()), targetInstance, err) return nil, nil } diff --git a/vendor/go.podman.io/image/v5/docker/docker_client.go b/vendor/go.podman.io/image/v5/docker/docker_client.go index b166bfbf2e..2f257076f5 100644 --- a/vendor/go.podman.io/image/v5/docker/docker_client.go +++ b/vendor/go.podman.io/image/v5/docker/docker_client.go @@ -972,6 +972,11 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { if c.sys != nil && c.sys.DockerProxyURL != nil { tr.Proxy = http.ProxyURL(c.sys.DockerProxyURL) } + if c.sys != nil && c.sys.DockerProxy != nil { + tr.Proxy = func(request *http.Request) (*url.URL, error) { + return c.sys.DockerProxy(request.URL) + } + } c.client = &http.Client{Transport: tr} ping := func(scheme string) error { diff --git a/vendor/go.podman.io/image/v5/internal/digests/digests.go b/vendor/go.podman.io/image/v5/internal/digests/digests.go new file mode 100644 index 0000000000..ca76ea3be7 --- /dev/null +++ b/vendor/go.podman.io/image/v5/internal/digests/digests.go @@ -0,0 +1,146 @@ +// Package digests provides an internal representation of users’ digest use preferences. +// +// Something like this _might_ be eventually made available as a public API: +// before doing so, carefully think whether the API should be modified before we commit to it. + +package digests + +import ( + "errors" + "fmt" + + "github.com/opencontainers/go-digest" +) + +// Options records users’ preferences for used digest algorithm usage. +// It is a value type and can be copied using ordinary assignment. +// +// It can only be created using one of the provided constructors. +type Options struct { + initialized bool // To prevent uses that don’t call a public constructor; this is necessary to enforce the .Available() promise. + + // If any of the fields below is set, it is guaranteed to be .Available(). + + mustUse digest.Algorithm // If not "", written digests must use this algorithm. + prefer digest.Algorithm // If not "", use this algorithm whenever possible. + defaultAlgo digest.Algorithm // If not "", use this algorithm if there is no reason to use anything else. +} + +// CanonicalDefault is Options which default to using digest.Canonical if there is no reason to use a different algorithm +// (e.g. when there is no pre-existing digest). +// +// The configuration can be customized using .WithPreferred() or .WithDefault(). +func CanonicalDefault() Options { + // This does not set .defaultAlgo so that .WithDefault() can be called (once). + return Options{ + initialized: true, + } +} + +// MustUse constructs Options which always use algo. +func MustUse(algo digest.Algorithm) (Options, error) { + // We don’t provide Options.WithMustUse because there is no other option that makes a difference + // once .mustUse is set. + if !algo.Available() { + return Options{}, fmt.Errorf("attempt to use an unavailable digest algorithm %q", algo.String()) + } + return Options{ + initialized: true, + mustUse: algo, + }, nil +} + +// WithPreferred returns a copy of o with a “preferred” algorithm set to algo. +// The preferred algorithm is used whenever possible (but if there is a strict requirement to use something else, it will be overridden). +func (o Options) WithPreferred(algo digest.Algorithm) (Options, error) { + if err := o.ensureInitialized(); err != nil { + return Options{}, err + } + if o.prefer != "" { + return Options{}, errors.New("digests.Options already have a 'prefer' algorithm configured") + } + + if !algo.Available() { + return Options{}, fmt.Errorf("attempt to use an unavailable digest algorithm %q", algo.String()) + } + o.prefer = algo + return o, nil +} + +// WithDefault returns a copy of o with a “default” algorithm set to algo. +// The default algorithm is used if there is no reason to use anything else (e.g. when there is no pre-existing digest). +func (o Options) WithDefault(algo digest.Algorithm) (Options, error) { + if err := o.ensureInitialized(); err != nil { + return Options{}, err + } + if o.defaultAlgo != "" { + return Options{}, errors.New("digests.Options already have a 'default' algorithm configured") + } + + if !algo.Available() { + return Options{}, fmt.Errorf("attempt to use an unavailable digest algorithm %q", algo.String()) + } + o.defaultAlgo = algo + return o, nil +} + +// ensureInitialized returns an error if o is not initialized. +func (o Options) ensureInitialized() error { + if !o.initialized { + return errors.New("internal error: use of uninitialized digests.Options") + } + return nil +} + +// Situation records the context in which a digest is being chosen. +type Situation struct { + Preexisting digest.Digest // If not "", a pre-existing digest value (frequently one which is cheaper to use than others) + CannotChangeAlgorithmReason string // The reason why we must use Preexisting, or "" if we can use other algorithms. +} + +// Choose chooses a digest algorithm based on the options and the situation. +func (o Options) Choose(s Situation) (digest.Algorithm, error) { + if err := o.ensureInitialized(); err != nil { + return "", err + } + + if s.CannotChangeAlgorithmReason != "" && s.Preexisting == "" { + return "", fmt.Errorf("internal error: digests.Situation.CannotChangeAlgorithmReason is set but Preexisting is empty") + } + + var choice digest.Algorithm // = what we want to use + switch { + case o.mustUse != "": + choice = o.mustUse + case s.CannotChangeAlgorithmReason != "": + choice = s.Preexisting.Algorithm() + if !choice.Available() { + return "", fmt.Errorf("existing digest uses unimplemented algorithm %s", choice) + } + case o.prefer != "": + choice = o.prefer + case s.Preexisting != "" && s.Preexisting.Algorithm().Available(): + choice = s.Preexisting.Algorithm() + case o.defaultAlgo != "": + choice = o.defaultAlgo + default: + choice = digest.Canonical // We assume digest.Canonical is always available. + } + + if s.CannotChangeAlgorithmReason != "" && choice != s.Preexisting.Algorithm() { + return "", fmt.Errorf("requested to always use digest algorithm %s but we cannot replace existing digest algorithm %s: %s", + choice, s.Preexisting.Algorithm(), s.CannotChangeAlgorithmReason) + } + + return choice, nil +} + +// MustUseSet returns an algorithm if o is set to always use a specific algorithm, "" if it is flexible. +func (o Options) MustUseSet() digest.Algorithm { + // We don’t do .ensureInitialized() because that would require an extra error value just for that. + // This should not be a part of any public API either way. + if o.mustUse != "" { + return o.mustUse + } + return "" +} diff --git a/vendor/go.podman.io/image/v5/oci/layout/oci_dest.go b/vendor/go.podman.io/image/v5/oci/layout/oci_dest.go index c4eee7db4d..16362b7779 100644 --- a/vendor/go.podman.io/image/v5/oci/layout/oci_dest.go +++ b/vendor/go.podman.io/image/v5/oci/layout/oci_dest.go @@ -50,9 +50,8 @@ func newImageDestination(sys *types.SystemContext, ref ociReference) (private.Im } } else { index = &imgspecv1.Index{ - Versioned: imgspec.Versioned{ - SchemaVersion: 2, - }, + Versioned: imgspec.Versioned{SchemaVersion: 2}, + MediaType: imgspecv1.MediaTypeImageIndex, Annotations: make(map[string]string), } } diff --git a/vendor/go.podman.io/image/v5/tarball/tarball_src.go b/vendor/go.podman.io/image/v5/tarball/tarball_src.go index a8af4b3256..3194f95203 100644 --- a/vendor/go.podman.io/image/v5/tarball/tarball_src.go +++ b/vendor/go.podman.io/image/v5/tarball/tarball_src.go @@ -179,9 +179,8 @@ func (r *tarballReference) NewImageSource(ctx context.Context, sys *types.System // Populate a manifest with the configuration blob and the layers. manifest := imgspecv1.Manifest{ - Versioned: imgspecs.Versioned{ - SchemaVersion: 2, - }, + Versioned: imgspecs.Versioned{SchemaVersion: 2}, + MediaType: imgspecv1.MediaTypeImageManifest, Config: imgspecv1.Descriptor{ Digest: configID, Size: int64(len(configBytes)), diff --git a/vendor/go.podman.io/image/v5/types/types.go b/vendor/go.podman.io/image/v5/types/types.go index 41f1a632e2..de25dabcdc 100644 --- a/vendor/go.podman.io/image/v5/types/types.go +++ b/vendor/go.podman.io/image/v5/types/types.go @@ -668,6 +668,10 @@ type SystemContext struct { DockerRegistryPushPrecomputeDigests bool // DockerProxyURL specifies proxy configuration schema (like socks5://username:password@ip:port) DockerProxyURL *url.URL + // DockerProxy is a function that determines the proxy URL for a given request URL. + // If set, this takes precedence over DockerProxyURL. The function should return the proxy URL to use, + // or nil if no proxy should be used for the given request. + DockerProxy func(reqURL *url.URL) (*url.URL, error) // === docker/daemon.Transport overrides === // A directory containing a CA certificate (ending with ".crt"), diff --git a/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go b/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go index 9a17f57014..8a3076a0f8 100644 --- a/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go +++ b/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go @@ -20,6 +20,12 @@ struct subid_range get_range(struct subid_range *ranges, int i) return ranges[i]; } +// helper for stderr to avoid referencing C.stderr from Go code, +// which breaks cgo on musl due to stderr being declared as FILE *const +static FILE *subid_stderr(void) { + return stderr; +} + #if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4) # define subid_init libsubid_init # define subid_get_uid_ranges get_subuid_ranges @@ -44,7 +50,7 @@ func readSubid(username string, isUser bool) (ranges, error) { } onceInit.Do(func() { - C.subid_init(C.CString("storage"), C.stderr) + C.subid_init(C.CString("storage"), C.subid_stderr()) }) cUsername := C.CString(username) diff --git a/vendor/modules.txt b/vendor/modules.txt index 0a6ecf870b..221394fa42 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -66,7 +66,7 @@ github.com/containerd/errdefs/pkg/internal/cause # github.com/containerd/log v0.1.0 ## explicit; go 1.20 github.com/containerd/log -# github.com/containerd/platforms v1.0.0-rc.1 +# github.com/containerd/platforms v1.0.0-rc.2 ## explicit; go 1.20 github.com/containerd/platforms # github.com/containerd/stargz-snapshotter/estargz v0.18.2 @@ -91,8 +91,8 @@ github.com/containernetworking/cni/pkg/version # github.com/containernetworking/plugins v1.9.0 ## explicit; go 1.24.2 github.com/containernetworking/plugins/pkg/ns -# github.com/containers/buildah v1.42.0 -## explicit; go 1.24.2 +# github.com/containers/buildah v1.42.1-0.20260126144005-964d45f717ce +## explicit; go 1.24.6 github.com/containers/buildah github.com/containers/buildah/bind github.com/containers/buildah/chroot @@ -102,11 +102,13 @@ github.com/containers/buildah/docker github.com/containers/buildah/imagebuildah github.com/containers/buildah/internal github.com/containers/buildah/internal/config +github.com/containers/buildah/internal/metadata github.com/containers/buildah/internal/mkcw github.com/containers/buildah/internal/mkcw/types github.com/containers/buildah/internal/open github.com/containers/buildah/internal/parse github.com/containers/buildah/internal/pty +github.com/containers/buildah/internal/sanitize github.com/containers/buildah/internal/sbom github.com/containers/buildah/internal/tmpdir github.com/containers/buildah/internal/util @@ -138,7 +140,7 @@ github.com/containers/libhvee/pkg/wmiext # github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 ## explicit github.com/containers/libtrust -# github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957 +# github.com/containers/luksy v0.0.0-20251208191447-ca096313c38f ## explicit; go 1.24.0 github.com/containers/luksy # github.com/containers/ocicrypt v1.2.1 @@ -293,7 +295,7 @@ github.com/felixge/httpsnoop ## explicit; go 1.17 github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify/internal -# github.com/fsouza/go-dockerclient v1.12.2 +# github.com/fsouza/go-dockerclient v1.12.3 ## explicit; go 1.24.0 github.com/fsouza/go-dockerclient # github.com/go-jose/go-jose/v4 v4.1.3 @@ -439,8 +441,8 @@ github.com/miekg/pkcs11 # github.com/mistifyio/go-zfs/v4 v4.0.0 ## explicit; go 1.14 github.com/mistifyio/go-zfs/v4 -# github.com/moby/buildkit v0.25.1 -## explicit; go 1.24.0 +# github.com/moby/buildkit v0.26.3 +## explicit; go 1.24.3 github.com/moby/buildkit/frontend/dockerfile/command github.com/moby/buildkit/frontend/dockerfile/parser github.com/moby/buildkit/frontend/dockerfile/shell @@ -837,7 +839,7 @@ go.podman.io/common/pkg/umask go.podman.io/common/pkg/util go.podman.io/common/pkg/version go.podman.io/common/version -# go.podman.io/image/v5 v5.38.1-0.20251209230740-724707234895 +# go.podman.io/image/v5 v5.38.1-0.20260123202709-b5801a635dfa ## explicit; go 1.24.6 go.podman.io/image/v5/copy go.podman.io/image/v5/directory @@ -850,6 +852,7 @@ go.podman.io/image/v5/docker/policyconfiguration go.podman.io/image/v5/docker/reference go.podman.io/image/v5/image go.podman.io/image/v5/internal/blobinfocache +go.podman.io/image/v5/internal/digests go.podman.io/image/v5/internal/image go.podman.io/image/v5/internal/imagedestination go.podman.io/image/v5/internal/imagedestination/impl @@ -911,7 +914,7 @@ go.podman.io/image/v5/transports go.podman.io/image/v5/transports/alltransports go.podman.io/image/v5/types go.podman.io/image/v5/version -# go.podman.io/storage v1.61.1-0.20251209230740-724707234895 +# go.podman.io/storage v1.61.1-0.20260123202709-b5801a635dfa ## explicit; go 1.24.0 go.podman.io/storage go.podman.io/storage/drivers