From a382bd329eae87d8ee963d33853e3300e609a616 Mon Sep 17 00:00:00 2001 From: Aditya R Date: Mon, 8 Aug 2022 11:26:09 +0530 Subject: vendor: bump buildah to v1.27.0 Bump buildah to v1.27.0 [NO NEW TESTS NEEDED] Signed-off-by: Aditya R --- go.mod | 4 +- go.sum | 16 +- vendor/github.com/containers/buildah/.cirrus.yml | 36 +- vendor/github.com/containers/buildah/CHANGELOG.md | 30 + vendor/github.com/containers/buildah/Makefile | 10 +- vendor/github.com/containers/buildah/bind/mount.go | 4 +- vendor/github.com/containers/buildah/buildah.go | 5 +- vendor/github.com/containers/buildah/changelog.txt | 29 + vendor/github.com/containers/buildah/chroot/run.go | 1474 ------------------- .../containers/buildah/chroot/run_linux.go | 1475 ++++++++++++++++++++ .../github.com/containers/buildah/copier/copier.go | 20 +- .../github.com/containers/buildah/define/build.go | 11 + .../github.com/containers/buildah/define/types.go | 67 +- vendor/github.com/containers/buildah/go.mod | 23 +- vendor/github.com/containers/buildah/go.sum | 59 +- vendor/github.com/containers/buildah/image.go | 2 +- .../containers/buildah/imagebuildah/build.go | 26 +- .../containers/buildah/imagebuildah/executor.go | 6 + .../buildah/imagebuildah/stage_executor.go | 383 ++++- vendor/github.com/containers/buildah/info.go | 38 +- .../github.com/containers/buildah/pkg/cli/build.go | 31 +- .../containers/buildah/pkg/cli/common.go | 8 +- .../containers/buildah/pkg/overlay/overlay.go | 8 +- .../containers/buildah/pkg/parse/parse.go | 21 +- .../containers/buildah/pkg/util/uptime_darwin.go | 10 + .../containers/buildah/pkg/util/uptime_freebsd.go | 25 + .../containers/buildah/pkg/util/uptime_linux.go | 28 + .../containers/buildah/pkg/util/uptime_windows.go | 10 + .../containers/buildah/pkg/util/version_unix.go | 19 + .../containers/buildah/pkg/util/version_windows.go | 10 + vendor/github.com/containers/buildah/run_common.go | 16 +- .../github.com/containers/buildah/run_freebsd.go | 1 + vendor/github.com/containers/buildah/run_linux.go | 4 +- vendor/github.com/containers/buildah/selinux.go | 3 +- vendor/github.com/containers/buildah/util.go | 2 +- vendor/github.com/containers/buildah/util/util.go | 18 +- .../containers/common/version/version.go | 2 +- vendor/modules.txt | 4 +- 38 files changed, 2223 insertions(+), 1715 deletions(-) delete mode 100644 vendor/github.com/containers/buildah/chroot/run.go create mode 100644 vendor/github.com/containers/buildah/chroot/run_linux.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/uptime_linux.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/uptime_windows.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/version_unix.go create mode 100644 vendor/github.com/containers/buildah/pkg/util/version_windows.go diff --git a/go.mod b/go.mod index 85b82c1a0..0c9e4c05f 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/container-orchestrated-devices/container-device-interface v0.4.0 github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v1.1.1 - github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab - github.com/containers/common v0.49.1-0.20220729221035-246800047d46 + github.com/containers/buildah v1.27.0 + github.com/containers/common v0.49.1 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.22.0 github.com/containers/ocicrypt v1.1.5 diff --git a/go.sum b/go.sum index 2862c502a..c8ada343e 100644 --- a/go.sum +++ b/go.sum @@ -392,15 +392,12 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE= github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= -github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab h1:NeI0DOkTf3Tn4OpdjMhMubAfTPs2oCO5jUY5wnpv4qk= -github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab/go.mod h1:iVtQtU6a+pbETBqIzg0oAWW3gTR1ItrAihJpLFFppmA= -github.com/containers/common v0.48.1-0.20220715075726-2ac10faca05a/go.mod h1:1dA7JPGoSi83kjf5H4NIrGANyLOULyvFqV1bwvYFEek= -github.com/containers/common v0.49.1-0.20220729221035-246800047d46 h1:BNNV+JlPYSmaa9rTapL9kh2JZrg7hmWwi/VrIY/KH1E= -github.com/containers/common v0.49.1-0.20220729221035-246800047d46/go.mod h1:ueM5hT0itKqCQvVJDs+EtjornAQtrHYxQJzP2gxeGIg= +github.com/containers/buildah v1.27.0 h1:LJ1ks7vKxwPzJGr5BWVvigbtVL9w7XeHtNEmiIOPJqI= +github.com/containers/buildah v1.27.0/go.mod h1:anH3ExvDXRNP9zLQCrOc1vWb5CrhqLF/aYFim4tslvA= +github.com/containers/common v0.49.1 h1:6y4/s2WwYxrv+Cox7fotOo316wuZI+iKKPUQweCYv50= +github.com/containers/common v0.49.1/go.mod h1:ueM5hT0itKqCQvVJDs+EtjornAQtrHYxQJzP2gxeGIg= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= -github.com/containers/image/v5 v5.21.2-0.20220712113758-29aec5f7bbbf/go.mod h1:0+N0ZM9mgMmoZZc6uNcgnEsbX85Ne7b29cIW5lqWwVU= -github.com/containers/image/v5 v5.21.2-0.20220714132403-2bb3f3e44c5c/go.mod h1:ykVAVRj4DhQNMHZDVU+KCtXjWBKpqiUe669eF0WBEEc= github.com/containers/image/v5 v5.22.0 h1:KemxPmD4D2YYOFZN2SgoTk7nBFcnwPiPW0MqjYtknSE= github.com/containers/image/v5 v5.22.0/go.mod h1:D8Ksv2RNB8qLJ7xe1P3rgJJOSQpahA6amv2Ax++/YO4= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= @@ -416,9 +413,6 @@ github.com/containers/psgo v1.7.2 h1:WbCvsY9w+nCv3j4der0mbD3PSRUv/W8l+G0YrZrdSDc github.com/containers/psgo v1.7.2/go.mod h1:SLpqxsPOHtTqRygjutCPXmeU2PoEFzV3gzJplN4BMx0= github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= github.com/containers/storage v1.38.0/go.mod h1:lBzt28gAk5ADZuRtwdndRJyqX22vnRaXmlF+7ktfMYc= -github.com/containers/storage v1.41.0/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s= -github.com/containers/storage v1.41.1-0.20220712184034-d26be7b27860/go.mod h1:uu6HCcijN30xRxW1ZuZRngwFGOlH5NpBWYiNBnDQNRw= -github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a/go.mod h1:4DfR+cPpkXKhJnnyydD3z82DXrnTBT63y1k0QWtM2i4= github.com/containers/storage v1.42.0 h1:zm2AQD4NDeTB3JQ8X+Wo5+VRqNB+b4ocEd7Qj6ylPJA= github.com/containers/storage v1.42.0/go.mod h1:JiUJwOgOo1dr2DdOUc1MRe2GCAXABYoYmOdPF8yvH78= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -1041,7 +1035,6 @@ github.com/klauspost/compress v1.14.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= @@ -1308,7 +1301,6 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab h1:Y github.com/opencontainers/runtime-spec v1.0.3-0.20211214071223-8958f93039ab/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 h1:Rf+QsQGxrYCia8mVyOPnoQZ+vJkZGL+ESWBDUM5s9cQ= github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7/go.mod h1:/tgP02fPXGHkU3/qKK1Y0Db4yqNyGm03vLq/mzHzcS4= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml index 70b59782a..857d80497 100644 --- a/vendor/github.com/containers/buildah/.cirrus.yml +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -28,12 +28,12 @@ env: # GCE project where images live IMAGE_PROJECT: "libpod-218412" FEDORA_NAME: "fedora-36" - PRIOR_FEDORA_NAME: "fedora-35" + #PRIOR_FEDORA_NAME: "fedora-35" UBUNTU_NAME: "ubuntu-2204" - IMAGE_SUFFIX: "c6193881921355776" + IMAGE_SUFFIX: "c6013173500215296" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" - PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" + #PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" IN_PODMAN_IMAGE: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}" @@ -66,15 +66,15 @@ meta_task: alias: meta container: - image: "quay.io/libpod/imgts:${IMAGE_SUFFIX}" # see contrib/imgts + image: "quay.io/libpod/imgts:latest" cpu: 1 memory: 1 env: # Space-separated list of images used by this repository state + # TODO: Re-add ${PRIOR_FEDORA_CACHE_IMAGE_NAME} when place back in use IMGNAMES: |- ${FEDORA_CACHE_IMAGE_NAME} - ${PRIOR_FEDORA_CACHE_IMAGE_NAME} ${UBUNTU_CACHE_IMAGE_NAME} build-push-${IMAGE_SUFFIX} BUILDID: "${CIRRUS_BUILD_ID}" @@ -215,10 +215,10 @@ integration_task: DISTRO_NV: "${FEDORA_NAME}" IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'vfs' - - env: - DISTRO_NV: "${PRIOR_FEDORA_NAME}" - IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'vfs' + # - env: + # DISTRO_NV: "${PRIOR_FEDORA_NAME}" + # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + # STORAGE_DRIVER: 'vfs' - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" @@ -228,10 +228,10 @@ integration_task: DISTRO_NV: "${FEDORA_NAME}" IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' - - env: - DISTRO_NV: "${PRIOR_FEDORA_NAME}" - IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'overlay' + # - env: + # DISTRO_NV: "${PRIOR_FEDORA_NAME}" + # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + # STORAGE_DRIVER: 'overlay' - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" @@ -272,11 +272,11 @@ integration_rootless_task: IMAGE_NAME: "${FEDORA_CACHE_IMAGE_NAME}" STORAGE_DRIVER: 'overlay' PRIV_NAME: rootless - - env: - DISTRO_NV: "${PRIOR_FEDORA_NAME}" - IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - STORAGE_DRIVER: 'overlay' - PRIV_NAME: rootless + # - env: + # DISTRO_NV: "${PRIOR_FEDORA_NAME}" + # IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + # STORAGE_DRIVER: 'overlay' + # PRIV_NAME: rootless - env: DISTRO_NV: "${UBUNTU_NAME}" IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}" diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md index 46e5dc9d6..667d5f81f 100644 --- a/vendor/github.com/containers/buildah/CHANGELOG.md +++ b/vendor/github.com/containers/buildah/CHANGELOG.md @@ -2,6 +2,36 @@ # Changelog +## v1.27.0 (2022-08-01) + + build: support filtering cache by duration using `--cache-ttl`. + build: support building from commit when using git repo as build context. + build: clean up git repos correctly when using subdirs. + build: add support for distributing cache to remote sources using `--cache-to` and `--cache-from`. + imagebuildah: optimize cache hits for `COPY` and `ADD` instructions. + build: support OCI hooks for ephemeral build containers. + build: add support for `--userns=auto`. + copier: add NoOverwriteNonDirDir option . + add initial support for building images using Buildah on FreeBSD. + multistage: this now skips the computing of unwanted stages to improve performance. + multiarch: support splitting build logs for `--platform` using `--logsplit`. + build: add support for building images where the base image has no history. + commit: allow disabling image history with `--omit-history`. + build: add support for renaming a device in rootless setups. + build: now supports additionalBuildContext in builds via the `--build-context` option. + build: `--output` produces artifacts even if the build container is not committed. + build: now accepts `-cpp-flag`, allowing users to pass in CPP flags when processing a Containerfile with C Preprocessor-like syntax. + build: now accepts a branch and a subdirectory when the build context is a git repository. + build: output now shows a progress bar while pushing and pulling images + build: now errors out if the path to Containerfile is a directory. + build: support building container images on environments that are rootless and without any valid login sessions. + fix: `--output` now generates artifacts even if the entire build is cached. + fix: `--output` generates artifacts only for the target stage in multi-stage builds. + fix,add: now fails on a bad HTTP response instead of writing to container + fix,squash: never use build cache when computing the last step of the last stage + fix,build,run: allow reusing secret more than once in different RUN steps + fix: compatibility with Docker build by making its --label and --annotate options set empty labels and annotations when given a name but no `=` or label value. + ## v1.26.0 (2022-05-04) imagebuildah,build: move deepcopy of args before we spawn goroutine diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index b40462eea..a3016e2ed 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -50,7 +50,7 @@ endif # Note: Uses the -N -l go compiler options to disable compiler optimizations # and inlining. Using these build options allows you to subsequently # use source debugging tools like delve. -all: bin/buildah bin/imgtype bin/copy docs +all: bin/buildah bin/imgtype bin/copy bin/tutorial docs # Update nix/nixpkgs.json its latest stable commit .PHONY: nixpkgs @@ -74,7 +74,8 @@ bin/buildah: $(SOURCES) cmd/buildah/*.go .PHONY: buildah buildah: bin/buildah -ALL_CROSS_TARGETS := $(addprefix bin/buildah.,$(subst /,.,$(shell $(GO) tool dist list))) +# TODO: remove `grep -v loong64` from `ALL_CROSS_TARGETS` once go.etcd.io/bbolt 1.3.7 is out. +ALL_CROSS_TARGETS := $(addprefix bin/buildah.,$(subst /,.,$(shell $(GO) tool dist list | grep -v loong64))) LINUX_CROSS_TARGETS := $(filter bin/buildah.linux.%,$(ALL_CROSS_TARGETS)) DARWIN_CROSS_TARGETS := $(filter bin/buildah.darwin.%,$(ALL_CROSS_TARGETS)) WINDOWS_CROSS_TARGETS := $(addsuffix .exe,$(filter bin/buildah.windows.%,$(ALL_CROSS_TARGETS))) @@ -92,6 +93,9 @@ bin/imgtype: $(SOURCES) tests/imgtype/imgtype.go bin/copy: $(SOURCES) tests/copy/copy.go $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/copy/copy.go +bin/tutorial: $(SOURCES) tests/tutorial/tutorial.go + $(GO_BUILD) $(BUILDAH_LDFLAGS) -o $@ $(BUILDFLAGS) ./tests/tutorial/tutorial.go + .PHONY: clean clean: $(RM) -r bin tests/testreport/testreport @@ -108,7 +112,7 @@ gopath: test $(shell pwd) = $(shell cd ../../../../src/github.com/containers/buildah ; pwd) codespell: - codespell -S Makefile,build,buildah,buildah.spec,imgtype,copy,AUTHORS,bin,vendor,.git,go.sum,CHANGELOG.md,changelog.txt,seccomp.json,.cirrus.yml,"*.xz,*.gz,*.tar,*.tgz,*ico,*.png,*.1,*.5,*.orig,*.rej" -L uint,iff,od,ERRO -w + codespell -S Makefile,buildah.spec.rpkg,AUTHORS,bin,vendor,.git,go.mod,go.sum,CHANGELOG.md,changelog.txt,seccomp.json,.cirrus.yml,"*.xz,*.gz,*.tar,*.tgz,*ico,*.png,*.1,*.5,*.orig,*.rej" -L uint,iff,od,ERRO -w .PHONY: validate validate: install.tools diff --git a/vendor/github.com/containers/buildah/bind/mount.go b/vendor/github.com/containers/buildah/bind/mount.go index 8e5ad458c..212be3ca8 100644 --- a/vendor/github.com/containers/buildah/bind/mount.go +++ b/vendor/github.com/containers/buildah/bind/mount.go @@ -150,7 +150,7 @@ func SetupIntermediateMountNamespace(spec *specs.Spec, bundlePath string) (unmou // Check if the source is a directory or something else. info, err := os.Stat(spec.Mounts[i].Source) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { logrus.Warnf("couldn't find %q on host to bind mount into container", spec.Mounts[i].Source) continue } @@ -269,7 +269,7 @@ func UnmountMountpoints(mountpoint string, mountpointsToRemove []string) error { mount := getMountByID(id) // check if this mountpoint is mounted if err := unix.Lstat(mount.Mountpoint, &st); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { logrus.Debugf("mountpoint %q is not present(?), skipping", mount.Mountpoint) continue } diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 5e6397f7f..41f1ba311 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -3,6 +3,7 @@ package buildah import ( "context" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -445,7 +446,7 @@ func OpenBuilderByPath(store storage.Store, path string) (*Builder, error) { } buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile)) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { logrus.Debugf("error reading %q: %v, ignoring container %q", filepath.Join(cdir, stateFile), err, container.ID) continue } @@ -482,7 +483,7 @@ func OpenAllBuilders(store storage.Store) (builders []*Builder, err error) { } buildstate, err := ioutil.ReadFile(filepath.Join(cdir, stateFile)) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { logrus.Debugf("error reading %q: %v, ignoring container %q", filepath.Join(cdir, stateFile), err, container.ID) continue } diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt index a8a010bcd..a6fa96acf 100644 --- a/vendor/github.com/containers/buildah/changelog.txt +++ b/vendor/github.com/containers/buildah/changelog.txt @@ -1,3 +1,32 @@ +- Changelog for v1.27.0 (2022-08-01) + * build: support filtering cache by duration using `--cache-ttl`. + * build: support building from commit when using git repo as build context. + * build: clean up git repos correctly when using subdirs. + * build: add support for distributing cache to remote sources using `--cache-to` and `--cache-from`. + * imagebuildah: optimize cache hits for `COPY` and `ADD` instructions. + * build: support OCI hooks for ephemeral build containers. + * build: add support for `--userns=auto`. + * copier: add NoOverwriteNonDirDir option . + * add initial support for building images using Buildah on FreeBSD. + * multistage: this now skips the computing of unwanted stages to improve performance. + * multiarch: support splitting build logs for `--platform` using `--logsplit`. + * build: add support for building images where the base image has no history. + * commit: allow disabling image history with `--omit-history`. + * build: add support for renaming a device in rootless setups. + * build: now supports additionalBuildContext in builds via the `--build-context` option. + * build: `--output` produces artifacts even if the build container is not committed. + * build: now accepts `-cpp-flag`, allowing users to pass in CPP flags when processing a Containerfile with C Preprocessor-like syntax. + * build: now accepts a branch and a subdirectory when the build context is a git repository. + * build: output now shows a progress bar while pushing and pulling images + * build: now errors out if the path to Containerfile is a directory. + * build: support building container images on environments that are rootless and without any valid login sessions. + * fix: `--output` now generates artifacts even if the entire build is cached. + * fix: `--output` generates artifacts only for the target stage in multi-stage builds. + * fix,add: now fails on a bad HTTP response instead of writing to container + * fix,squash: never use build cache when computing the last step of the last stage + * fix,build,run: allow reusing secret more than once in different RUN steps + * fix: compatibility with Docker build by making its --label and --annotate options set empty labels and annotations when given a name but no `=` or label value. + - Changelog for v1.26.0 (2022-05-04) * imagebuildah,build: move deepcopy of args before we spawn goroutine * Vendor in containers/storage v1.40.2 diff --git a/vendor/github.com/containers/buildah/chroot/run.go b/vendor/github.com/containers/buildah/chroot/run.go deleted file mode 100644 index 809a70131..000000000 --- a/vendor/github.com/containers/buildah/chroot/run.go +++ /dev/null @@ -1,1474 +0,0 @@ -//go:build linux -// +build linux - -package chroot - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "os/signal" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" - "syscall" - "time" - "unsafe" - - "github.com/containers/buildah/bind" - "github.com/containers/buildah/copier" - "github.com/containers/buildah/util" - "github.com/containers/storage/pkg/ioutils" - "github.com/containers/storage/pkg/mount" - "github.com/containers/storage/pkg/reexec" - "github.com/containers/storage/pkg/unshare" - "github.com/opencontainers/runc/libcontainer/apparmor" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/sirupsen/logrus" - "github.com/syndtr/gocapability/capability" - "golang.org/x/sys/unix" - "golang.org/x/term" -) - -const ( - // runUsingChrootCommand is a command we use as a key for reexec - runUsingChrootCommand = "buildah-chroot-runtime" - // runUsingChrootExec is a command we use as a key for reexec - runUsingChrootExecCommand = "buildah-chroot-exec" -) - -var ( - rlimitsMap = map[string]int{ - "RLIMIT_AS": unix.RLIMIT_AS, - "RLIMIT_CORE": unix.RLIMIT_CORE, - "RLIMIT_CPU": unix.RLIMIT_CPU, - "RLIMIT_DATA": unix.RLIMIT_DATA, - "RLIMIT_FSIZE": unix.RLIMIT_FSIZE, - "RLIMIT_LOCKS": unix.RLIMIT_LOCKS, - "RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK, - "RLIMIT_MSGQUEUE": unix.RLIMIT_MSGQUEUE, - "RLIMIT_NICE": unix.RLIMIT_NICE, - "RLIMIT_NOFILE": unix.RLIMIT_NOFILE, - "RLIMIT_NPROC": unix.RLIMIT_NPROC, - "RLIMIT_RSS": unix.RLIMIT_RSS, - "RLIMIT_RTPRIO": unix.RLIMIT_RTPRIO, - "RLIMIT_RTTIME": unix.RLIMIT_RTTIME, - "RLIMIT_SIGPENDING": unix.RLIMIT_SIGPENDING, - "RLIMIT_STACK": unix.RLIMIT_STACK, - } - rlimitsReverseMap = map[int]string{} -) - -func init() { - reexec.Register(runUsingChrootCommand, runUsingChrootMain) - reexec.Register(runUsingChrootExecCommand, runUsingChrootExecMain) - for limitName, limitNumber := range rlimitsMap { - rlimitsReverseMap[limitNumber] = limitName - } -} - -type runUsingChrootSubprocOptions struct { - Spec *specs.Spec - BundlePath string - UIDMappings []syscall.SysProcIDMap - GIDMappings []syscall.SysProcIDMap -} - -type runUsingChrootExecSubprocOptions struct { - Spec *specs.Spec - BundlePath string -} - -// RunUsingChroot runs a chrooted process, using some of the settings from the -// passed-in spec, and using the specified bundlePath to hold temporary files, -// directories, and mountpoints. -func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reader, stdout, stderr io.Writer) (err error) { - var confwg sync.WaitGroup - var homeFound bool - for _, env := range spec.Process.Env { - if strings.HasPrefix(env, "HOME=") { - homeFound = true - break - } - } - if !homeFound { - spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir)) - } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - // Write the runtime configuration, mainly for debugging. - specbytes, err := json.Marshal(spec) - if err != nil { - return err - } - if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0600); err != nil { - return fmt.Errorf("error storing runtime configuration: %w", err) - } - logrus.Debugf("config = %v", string(specbytes)) - - // Default to using stdin/stdout/stderr if we weren't passed objects to use. - if stdin == nil { - stdin = os.Stdin - } - if stdout == nil { - stdout = os.Stdout - } - if stderr == nil { - stderr = os.Stderr - } - - // Create a pipe for passing configuration down to the next process. - preader, pwriter, err := os.Pipe() - if err != nil { - return fmt.Errorf("error creating configuration pipe: %w", err) - } - config, conferr := json.Marshal(runUsingChrootSubprocOptions{ - Spec: spec, - BundlePath: bundlePath, - }) - if conferr != nil { - return fmt.Errorf("error encoding configuration for %q: %w", runUsingChrootCommand, conferr) - } - - // Set our terminal's mode to raw, to pass handling of special - // terminal input to the terminal in the container. - if spec.Process.Terminal && term.IsTerminal(unix.Stdin) { - state, err := term.MakeRaw(unix.Stdin) - if err != nil { - logrus.Warnf("error setting terminal state: %v", err) - } else { - defer func() { - if err = term.Restore(unix.Stdin, state); err != nil { - logrus.Errorf("unable to restore terminal state: %v", err) - } - }() - } - } - - // Raise any resource limits that are higher than they are now, before - // we drop any more privileges. - if err = setRlimits(spec, false, true); err != nil { - return err - } - - // Start the grandparent subprocess. - cmd := unshare.Command(runUsingChrootCommand) - setPdeathsig(cmd.Cmd) - cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr - cmd.Dir = "/" - cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())} - - interrupted := make(chan os.Signal, 100) - cmd.Hook = func(int) error { - signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) - go func() { - for receivedSignal := range interrupted { - if err := cmd.Process.Signal(receivedSignal); err != nil { - logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal) - } - } - }() - return nil - } - - logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd) - confwg.Add(1) - go func() { - _, conferr = io.Copy(pwriter, bytes.NewReader(config)) - pwriter.Close() - confwg.Done() - }() - cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...) - err = cmd.Run() - confwg.Wait() - signal.Stop(interrupted) - close(interrupted) - if err == nil { - return conferr - } - return err -} - -// main() for grandparent subprocess. Its main job is to shuttle stdio back -// and forth, managing a pseudo-terminal if we want one, for our child, the -// parent subprocess. -func runUsingChrootMain() { - var options runUsingChrootSubprocOptions - - runtime.LockOSThread() - - // Set logging. - if level := os.Getenv("LOGLEVEL"); level != "" { - if ll, err := strconv.Atoi(level); err == nil { - logrus.SetLevel(logrus.Level(ll)) - } - os.Unsetenv("LOGLEVEL") - } - - // Unpack our configuration. - confPipe := os.NewFile(3, "confpipe") - if confPipe == nil { - fmt.Fprintf(os.Stderr, "error reading options pipe\n") - os.Exit(1) - } - defer confPipe.Close() - if err := json.NewDecoder(confPipe).Decode(&options); err != nil { - fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err) - os.Exit(1) - } - - if options.Spec == nil || options.Spec.Process == nil { - fmt.Fprintf(os.Stderr, "invalid options spec in runUsingChrootMain\n") - os.Exit(1) - } - - // Prepare to shuttle stdio back and forth. - rootUID32, rootGID32, err := util.GetHostRootIDs(options.Spec) - if err != nil { - logrus.Errorf("error determining ownership for container stdio") - os.Exit(1) - } - rootUID := int(rootUID32) - rootGID := int(rootGID32) - relays := make(map[int]int) - closeOnceRunning := []*os.File{} - var ctty *os.File - var stdin io.Reader - var stdinCopy io.WriteCloser - var stdout io.Writer - var stderr io.Writer - fdDesc := make(map[int]string) - if options.Spec.Process.Terminal { - // Create a pseudo-terminal -- open a copy of the master side. - ptyMasterFd, err := unix.Open("/dev/ptmx", os.O_RDWR, 0600) - if err != nil { - logrus.Errorf("error opening PTY master using /dev/ptmx: %v", err) - os.Exit(1) - } - // Set the kernel's lock to "unlocked". - locked := 0 - if result, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&locked))); int(result) == -1 { - logrus.Errorf("error unlocking PTY descriptor: %v", err) - os.Exit(1) - } - // Get a handle for the other end. - ptyFd, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCGPTPEER, unix.O_RDWR|unix.O_NOCTTY) - if int(ptyFd) == -1 { - if errno, isErrno := err.(syscall.Errno); !isErrno || (errno != syscall.EINVAL && errno != syscall.ENOTTY) { - logrus.Errorf("error getting PTY descriptor: %v", err) - os.Exit(1) - } - // EINVAL means the kernel's too old to understand TIOCGPTPEER. Try TIOCGPTN. - ptyN, err := unix.IoctlGetInt(ptyMasterFd, unix.TIOCGPTN) - if err != nil { - logrus.Errorf("error getting PTY number: %v", err) - os.Exit(1) - } - ptyName := fmt.Sprintf("/dev/pts/%d", ptyN) - fd, err := unix.Open(ptyName, unix.O_RDWR|unix.O_NOCTTY, 0620) - if err != nil { - logrus.Errorf("error opening PTY %q: %v", ptyName, err) - os.Exit(1) - } - ptyFd = uintptr(fd) - } - // Make notes about what's going where. - relays[ptyMasterFd] = unix.Stdout - relays[unix.Stdin] = ptyMasterFd - fdDesc[ptyMasterFd] = "container terminal" - fdDesc[unix.Stdin] = "stdin" - fdDesc[unix.Stdout] = "stdout" - winsize := &unix.Winsize{} - // Set the pseudoterminal's size to the configured size, or our own. - if options.Spec.Process.ConsoleSize != nil { - // Use configured sizes. - winsize.Row = uint16(options.Spec.Process.ConsoleSize.Height) - winsize.Col = uint16(options.Spec.Process.ConsoleSize.Width) - } else { - if term.IsTerminal(unix.Stdin) { - // Use the size of our terminal. - winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ) - if err != nil { - logrus.Debugf("error reading current terminal's size") - winsize.Row = 0 - winsize.Col = 0 - } - } - } - if winsize.Row != 0 && winsize.Col != 0 { - if err = unix.IoctlSetWinsize(int(ptyFd), unix.TIOCSWINSZ, winsize); err != nil { - logrus.Warnf("error setting terminal size for pty") - } - // FIXME - if we're connected to a terminal, we should - // be passing the updated terminal size down when we - // receive a SIGWINCH. - } - // Open an *os.File object that we can pass to our child. - ctty = os.NewFile(ptyFd, "/dev/tty") - // Set ownership for the PTY. - if err = ctty.Chown(rootUID, rootGID); err != nil { - var cttyInfo unix.Stat_t - err2 := unix.Fstat(int(ptyFd), &cttyInfo) - from := "" - op := "setting" - if err2 == nil { - op = "changing" - from = fmt.Sprintf("from %d/%d ", cttyInfo.Uid, cttyInfo.Gid) - } - logrus.Warnf("error %s ownership of container PTY %sto %d/%d: %v", op, from, rootUID, rootGID, err) - } - // Set permissions on the PTY. - if err = ctty.Chmod(0620); err != nil { - logrus.Errorf("error setting permissions of container PTY: %v", err) - os.Exit(1) - } - // Make a note that our child (the parent subprocess) should - // have the PTY connected to its stdio, and that we should - // close it once it's running. - stdin = ctty - stdout = ctty - stderr = ctty - closeOnceRunning = append(closeOnceRunning, ctty) - } else { - // Create pipes for stdio. - stdinRead, stdinWrite, err := os.Pipe() - if err != nil { - logrus.Errorf("error opening pipe for stdin: %v", err) - } - stdoutRead, stdoutWrite, err := os.Pipe() - if err != nil { - logrus.Errorf("error opening pipe for stdout: %v", err) - } - stderrRead, stderrWrite, err := os.Pipe() - if err != nil { - logrus.Errorf("error opening pipe for stderr: %v", err) - } - // Make notes about what's going where. - relays[unix.Stdin] = int(stdinWrite.Fd()) - relays[int(stdoutRead.Fd())] = unix.Stdout - relays[int(stderrRead.Fd())] = unix.Stderr - fdDesc[int(stdinWrite.Fd())] = "container stdin pipe" - fdDesc[int(stdoutRead.Fd())] = "container stdout pipe" - fdDesc[int(stderrRead.Fd())] = "container stderr pipe" - fdDesc[unix.Stdin] = "stdin" - fdDesc[unix.Stdout] = "stdout" - fdDesc[unix.Stderr] = "stderr" - // Set ownership for the pipes. - if err = stdinRead.Chown(rootUID, rootGID); err != nil { - logrus.Errorf("error setting ownership of container stdin pipe: %v", err) - os.Exit(1) - } - if err = stdoutWrite.Chown(rootUID, rootGID); err != nil { - logrus.Errorf("error setting ownership of container stdout pipe: %v", err) - os.Exit(1) - } - if err = stderrWrite.Chown(rootUID, rootGID); err != nil { - logrus.Errorf("error setting ownership of container stderr pipe: %v", err) - os.Exit(1) - } - // Make a note that our child (the parent subprocess) should - // have the pipes connected to its stdio, and that we should - // close its ends of them once it's running. - stdin = stdinRead - stdout = stdoutWrite - stderr = stderrWrite - closeOnceRunning = append(closeOnceRunning, stdinRead, stdoutWrite, stderrWrite) - stdinCopy = stdinWrite - defer stdoutRead.Close() - defer stderrRead.Close() - } - for readFd, writeFd := range relays { - if err := unix.SetNonblock(readFd, true); err != nil { - logrus.Errorf("error setting descriptor %d (%s) non-blocking: %v", readFd, fdDesc[readFd], err) - return - } - if err := unix.SetNonblock(writeFd, false); err != nil { - logrus.Errorf("error setting descriptor %d (%s) blocking: %v", relays[writeFd], fdDesc[writeFd], err) - return - } - } - if err := unix.SetNonblock(relays[unix.Stdin], true); err != nil { - logrus.Errorf("error setting %d to nonblocking: %v", relays[unix.Stdin], err) - } - go func() { - buffers := make(map[int]*bytes.Buffer) - for _, writeFd := range relays { - buffers[writeFd] = new(bytes.Buffer) - } - pollTimeout := -1 - stdinClose := false - for len(relays) > 0 { - fds := make([]unix.PollFd, 0, len(relays)) - for fd := range relays { - fds = append(fds, unix.PollFd{Fd: int32(fd), Events: unix.POLLIN | unix.POLLHUP}) - } - _, err := unix.Poll(fds, pollTimeout) - if !util.LogIfNotRetryable(err, fmt.Sprintf("poll: %v", err)) { - return - } - removeFds := make(map[int]struct{}) - for _, rfd := range fds { - if rfd.Revents&unix.POLLHUP == unix.POLLHUP { - removeFds[int(rfd.Fd)] = struct{}{} - } - if rfd.Revents&unix.POLLNVAL == unix.POLLNVAL { - logrus.Debugf("error polling descriptor %s: closed?", fdDesc[int(rfd.Fd)]) - removeFds[int(rfd.Fd)] = struct{}{} - } - if rfd.Revents&unix.POLLIN == 0 { - if stdinClose && stdinCopy == nil { - continue - } - continue - } - b := make([]byte, 8192) - nread, err := unix.Read(int(rfd.Fd), b) - util.LogIfNotRetryable(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err)) - if nread > 0 { - if wfd, ok := relays[int(rfd.Fd)]; ok { - nwritten, err := buffers[wfd].Write(b[:nread]) - if err != nil { - logrus.Debugf("buffer: %v", err) - continue - } - if nwritten != nread { - logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nread, nwritten) - continue - } - } - // If this is the last of the data we'll be able to read - // from this descriptor, read as much as there is to read. - for rfd.Revents&unix.POLLHUP == unix.POLLHUP { - nr, err := unix.Read(int(rfd.Fd), b) - util.LogIfUnexpectedWhileDraining(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err)) - if nr <= 0 { - break - } - if wfd, ok := relays[int(rfd.Fd)]; ok { - nwritten, err := buffers[wfd].Write(b[:nr]) - if err != nil { - logrus.Debugf("buffer: %v", err) - break - } - if nwritten != nr { - logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nr, nwritten) - break - } - } - } - } - if nread == 0 { - removeFds[int(rfd.Fd)] = struct{}{} - } - } - pollTimeout = -1 - for wfd, buffer := range buffers { - if buffer.Len() > 0 { - nwritten, err := unix.Write(wfd, buffer.Bytes()) - util.LogIfNotRetryable(err, fmt.Sprintf("write %s: %v", fdDesc[wfd], err)) - if nwritten >= 0 { - _ = buffer.Next(nwritten) - } - } - if buffer.Len() > 0 { - pollTimeout = 100 - } - if wfd == relays[unix.Stdin] && stdinClose && buffer.Len() == 0 { - stdinCopy.Close() - delete(relays, unix.Stdin) - } - } - for rfd := range removeFds { - if rfd == unix.Stdin { - buffer, found := buffers[relays[unix.Stdin]] - if found && buffer.Len() > 0 { - stdinClose = true - continue - } - } - if !options.Spec.Process.Terminal && rfd == unix.Stdin { - stdinCopy.Close() - } - delete(relays, rfd) - } - } - }() - - // Set up mounts and namespaces, and run the parent subprocess. - status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, closeOnceRunning) - if err != nil { - fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err) - os.Exit(1) - } - - // Pass the process's exit status back to the caller by exiting with the same status. - if status.Exited() { - if status.ExitStatus() != 0 { - fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", status.ExitStatus()) - } - os.Exit(status.ExitStatus()) - } else if status.Signaled() { - fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", status.Signal()) - os.Exit(1) - } -} - -// runUsingChroot, still in the grandparent process, sets up various bind -// mounts and then runs the parent process in its own user namespace with the -// necessary ID mappings. -func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io.Reader, stdout, stderr io.Writer, closeOnceRunning []*os.File) (wstatus unix.WaitStatus, err error) { - var confwg sync.WaitGroup - - // Create a new mount namespace for ourselves and bind mount everything to a new location. - undoIntermediates, err := bind.SetupIntermediateMountNamespace(spec, bundlePath) - if err != nil { - return 1, err - } - defer func() { - if undoErr := undoIntermediates(); undoErr != nil { - logrus.Debugf("error cleaning up intermediate mount NS: %v", err) - } - }() - - // Bind mount in our filesystems. - undoChroots, err := setupChrootBindMounts(spec, bundlePath) - if err != nil { - return 1, err - } - defer func() { - if undoErr := undoChroots(); undoErr != nil { - logrus.Debugf("error cleaning up intermediate chroot bind mounts: %v", err) - } - }() - - // Create a pipe for passing configuration down to the next process. - preader, pwriter, err := os.Pipe() - if err != nil { - return 1, fmt.Errorf("error creating configuration pipe: %w", err) - } - config, conferr := json.Marshal(runUsingChrootExecSubprocOptions{ - Spec: spec, - BundlePath: bundlePath, - }) - if conferr != nil { - fmt.Fprintf(os.Stderr, "error re-encoding configuration for %q", runUsingChrootExecCommand) - os.Exit(1) - } - - // Apologize for the namespace configuration that we're about to ignore. - logNamespaceDiagnostics(spec) - - // If we have configured ID mappings, set them here so that they can apply to the child. - hostUidmap, hostGidmap, err := unshare.GetHostIDMappings("") - if err != nil { - return 1, err - } - uidmap, gidmap := spec.Linux.UIDMappings, spec.Linux.GIDMappings - if len(uidmap) == 0 { - // No UID mappings are configured for the container. Borrow our parent's mappings. - uidmap = append([]specs.LinuxIDMapping{}, hostUidmap...) - for i := range uidmap { - uidmap[i].HostID = uidmap[i].ContainerID - } - } - if len(gidmap) == 0 { - // No GID mappings are configured for the container. Borrow our parent's mappings. - gidmap = append([]specs.LinuxIDMapping{}, hostGidmap...) - for i := range gidmap { - gidmap[i].HostID = gidmap[i].ContainerID - } - } - - // Start the parent subprocess. - cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...) - setPdeathsig(cmd.Cmd) - cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr - cmd.Dir = "/" - cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())} - cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS - requestedUserNS := false - for _, ns := range spec.Linux.Namespaces { - if ns.Type == specs.UserNamespace { - requestedUserNS = true - } - } - if len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0 || requestedUserNS { - cmd.UnshareFlags = cmd.UnshareFlags | syscall.CLONE_NEWUSER - cmd.UidMappings = uidmap - cmd.GidMappings = gidmap - cmd.GidMappingsEnableSetgroups = true - } - if ctty != nil { - cmd.Setsid = true - cmd.Ctty = ctty - } - cmd.OOMScoreAdj = spec.Process.OOMScoreAdj - cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...) - interrupted := make(chan os.Signal, 100) - cmd.Hook = func(int) error { - for _, f := range closeOnceRunning { - f.Close() - } - signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) - go func() { - for receivedSignal := range interrupted { - if err := cmd.Process.Signal(receivedSignal); err != nil { - logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal) - } - } - }() - return nil - } - - logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd) - confwg.Add(1) - go func() { - _, conferr = io.Copy(pwriter, bytes.NewReader(config)) - pwriter.Close() - confwg.Done() - }() - err = cmd.Run() - confwg.Wait() - signal.Stop(interrupted) - close(interrupted) - if err != nil { - if exitError, ok := err.(*exec.ExitError); ok { - if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { - if waitStatus.Exited() { - if waitStatus.ExitStatus() != 0 { - fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus()) - } - os.Exit(waitStatus.ExitStatus()) - } else if waitStatus.Signaled() { - fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal()) - os.Exit(1) - } - } - } - fmt.Fprintf(os.Stderr, "process exited with error: %v", err) - os.Exit(1) - } - - return 0, nil -} - -// main() for parent subprocess. Its main job is to try to make our -// environment look like the one described by the runtime configuration blob, -// and then launch the intended command as a child. -func runUsingChrootExecMain() { - args := os.Args[1:] - var options runUsingChrootExecSubprocOptions - var err error - - runtime.LockOSThread() - - // Set logging. - if level := os.Getenv("LOGLEVEL"); level != "" { - if ll, err := strconv.Atoi(level); err == nil { - logrus.SetLevel(logrus.Level(ll)) - } - os.Unsetenv("LOGLEVEL") - } - - // Unpack our configuration. - confPipe := os.NewFile(3, "confpipe") - if confPipe == nil { - fmt.Fprintf(os.Stderr, "error reading options pipe\n") - os.Exit(1) - } - defer confPipe.Close() - if err := json.NewDecoder(confPipe).Decode(&options); err != nil { - fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err) - os.Exit(1) - } - - // Set the hostname. We're already in a distinct UTS namespace and are admins in the user - // namespace which created it, so we shouldn't get a permissions error, but seccomp policy - // might deny our attempt to call sethostname() anyway, so log a debug message for that. - if options.Spec == nil || options.Spec.Process == nil { - fmt.Fprintf(os.Stderr, "invalid options spec passed in\n") - os.Exit(1) - } - - if options.Spec.Hostname != "" { - if err := unix.Sethostname([]byte(options.Spec.Hostname)); err != nil { - logrus.Debugf("failed to set hostname %q for process: %v", options.Spec.Hostname, err) - } - } - - // Try to chroot into the root. Do this before we potentially block the syscall via the - // seccomp profile. - var oldst, newst unix.Stat_t - if err := unix.Stat(options.Spec.Root.Path, &oldst); err != nil { - fmt.Fprintf(os.Stderr, "error stat()ing intended root directory %q: %v\n", options.Spec.Root.Path, err) - os.Exit(1) - } - if err := unix.Chdir(options.Spec.Root.Path); err != nil { - fmt.Fprintf(os.Stderr, "error chdir()ing to intended root directory %q: %v\n", options.Spec.Root.Path, err) - os.Exit(1) - } - if err := unix.Chroot(options.Spec.Root.Path); err != nil { - fmt.Fprintf(os.Stderr, "error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err) - os.Exit(1) - } - if err := unix.Stat("/", &newst); err != nil { - fmt.Fprintf(os.Stderr, "error stat()ing current root directory: %v\n", err) - os.Exit(1) - } - if oldst.Dev != newst.Dev || oldst.Ino != newst.Ino { - fmt.Fprintf(os.Stderr, "unknown error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err) - os.Exit(1) - } - logrus.Debugf("chrooted into %q", options.Spec.Root.Path) - - // not doing because it's still shared: creating devices - // not doing because it's not applicable: setting annotations - // not doing because it's still shared: setting sysctl settings - // not doing because cgroupfs is read only: configuring control groups - // -> this means we can use the freezer to make sure there aren't any lingering processes - // -> this means we ignore cgroups-based controls - // not doing because we don't set any in the config: running hooks - // not doing because we don't set it in the config: setting rootfs read-only - // not doing because we don't set it in the config: setting rootfs propagation - logrus.Debugf("setting apparmor profile") - if err = setApparmorProfile(options.Spec); err != nil { - fmt.Fprintf(os.Stderr, "error setting apparmor profile for process: %v\n", err) - os.Exit(1) - } - if err = setSelinuxLabel(options.Spec); err != nil { - fmt.Fprintf(os.Stderr, "error setting SELinux label for process: %v\n", err) - os.Exit(1) - } - - logrus.Debugf("setting resource limits") - if err = setRlimits(options.Spec, false, false); err != nil { - fmt.Fprintf(os.Stderr, "error setting process resource limits for process: %v\n", err) - os.Exit(1) - } - - // Try to change to the directory. - cwd := options.Spec.Process.Cwd - if !filepath.IsAbs(cwd) { - cwd = "/" + cwd - } - cwd = filepath.Clean(cwd) - if err := unix.Chdir("/"); err != nil { - fmt.Fprintf(os.Stderr, "error chdir()ing into new root directory %q: %v\n", options.Spec.Root.Path, err) - os.Exit(1) - } - if err := unix.Chdir(cwd); err != nil { - fmt.Fprintf(os.Stderr, "error chdir()ing into directory %q under root %q: %v\n", cwd, options.Spec.Root.Path, err) - os.Exit(1) - } - logrus.Debugf("changed working directory to %q", cwd) - - // Drop privileges. - user := options.Spec.Process.User - if len(user.AdditionalGids) > 0 { - gids := make([]int, len(user.AdditionalGids)) - for i := range user.AdditionalGids { - gids[i] = int(user.AdditionalGids[i]) - } - logrus.Debugf("setting supplemental groups") - if err = syscall.Setgroups(gids); err != nil { - fmt.Fprintf(os.Stderr, "error setting supplemental groups list: %v", err) - os.Exit(1) - } - } else { - setgroups, _ := ioutil.ReadFile("/proc/self/setgroups") - if strings.Trim(string(setgroups), "\n") != "deny" { - logrus.Debugf("clearing supplemental groups") - if err = syscall.Setgroups([]int{}); err != nil { - fmt.Fprintf(os.Stderr, "error clearing supplemental groups list: %v", err) - os.Exit(1) - } - } - } - - logrus.Debugf("setting gid") - if err = syscall.Setresgid(int(user.GID), int(user.GID), int(user.GID)); err != nil { - fmt.Fprintf(os.Stderr, "error setting GID: %v", err) - os.Exit(1) - } - - if err = setSeccomp(options.Spec); err != nil { - fmt.Fprintf(os.Stderr, "error setting seccomp filter for process: %v\n", err) - os.Exit(1) - } - - logrus.Debugf("setting capabilities") - var keepCaps []string - if user.UID != 0 { - keepCaps = []string{"CAP_SETUID"} - } - if err := setCapabilities(options.Spec, keepCaps...); err != nil { - fmt.Fprintf(os.Stderr, "error setting capabilities for process: %v\n", err) - os.Exit(1) - } - - logrus.Debugf("setting uid") - if err = syscall.Setresuid(int(user.UID), int(user.UID), int(user.UID)); err != nil { - fmt.Fprintf(os.Stderr, "error setting UID: %v", err) - os.Exit(1) - } - - // Actually run the specified command. - cmd := exec.Command(args[0], args[1:]...) - setPdeathsig(cmd) - cmd.Env = options.Spec.Process.Env - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - cmd.Dir = cwd - logrus.Debugf("Running %#v (PATH = %q)", cmd, os.Getenv("PATH")) - interrupted := make(chan os.Signal, 100) - if err = cmd.Start(); err != nil { - fmt.Fprintf(os.Stderr, "process failed to start with error: %v", err) - } - go func() { - for range interrupted { - if err := cmd.Process.Signal(syscall.SIGKILL); err != nil { - logrus.Infof("%v while attempting to send SIGKILL to child process", err) - } - } - }() - signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) - err = cmd.Wait() - signal.Stop(interrupted) - close(interrupted) - if err != nil { - if exitError, ok := err.(*exec.ExitError); ok { - if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { - if waitStatus.Exited() { - if waitStatus.ExitStatus() != 0 { - fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus()) - } - os.Exit(waitStatus.ExitStatus()) - } else if waitStatus.Signaled() { - fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal()) - os.Exit(1) - } - } - } - fmt.Fprintf(os.Stderr, "process exited with error: %v", err) - os.Exit(1) - } -} - -// logNamespaceDiagnostics knows which namespaces we want to create. -// Output debug messages when that differs from what we're being asked to do. -func logNamespaceDiagnostics(spec *specs.Spec) { - sawMountNS := false - sawUTSNS := false - for _, ns := range spec.Linux.Namespaces { - switch ns.Type { - case specs.CgroupNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join cgroup namespace, sorry about that") - } else { - logrus.Debugf("unable to create cgroup namespace, sorry about that") - } - case specs.IPCNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join IPC namespace, sorry about that") - } else { - logrus.Debugf("unable to create IPC namespace, sorry about that") - } - case specs.MountNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join mount namespace %q, creating a new one", ns.Path) - } - sawMountNS = true - case specs.NetworkNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join network namespace, sorry about that") - } else { - logrus.Debugf("unable to create network namespace, sorry about that") - } - case specs.PIDNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join PID namespace, sorry about that") - } else { - logrus.Debugf("unable to create PID namespace, sorry about that") - } - case specs.UserNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join user namespace, sorry about that") - } - case specs.UTSNamespace: - if ns.Path != "" { - logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path) - } - sawUTSNS = true - } - } - if !sawMountNS { - logrus.Debugf("mount namespace not requested, but creating a new one anyway") - } - if !sawUTSNS { - logrus.Debugf("UTS namespace not requested, but creating a new one anyway") - } -} - -// setApparmorProfile sets the apparmor profile for ourselves, and hopefully any child processes that we'll start. -func setApparmorProfile(spec *specs.Spec) error { - if !apparmor.IsEnabled() || spec.Process.ApparmorProfile == "" { - return nil - } - if err := apparmor.ApplyProfile(spec.Process.ApparmorProfile); err != nil { - return fmt.Errorf("error setting apparmor profile to %q: %w", spec.Process.ApparmorProfile, err) - } - return nil -} - -// setCapabilities sets capabilities for ourselves, to be more or less inherited by any processes that we'll start. -func setCapabilities(spec *specs.Spec, keepCaps ...string) error { - currentCaps, err := capability.NewPid2(0) - if err != nil { - return fmt.Errorf("error reading capabilities of current process: %w", err) - } - if err := currentCaps.Load(); err != nil { - return fmt.Errorf("error loading capabilities: %w", err) - } - caps, err := capability.NewPid2(0) - if err != nil { - return fmt.Errorf("error reading capabilities of current process: %w", err) - } - capMap := map[capability.CapType][]string{ - capability.BOUNDING: spec.Process.Capabilities.Bounding, - capability.EFFECTIVE: spec.Process.Capabilities.Effective, - capability.INHERITABLE: []string{}, - capability.PERMITTED: spec.Process.Capabilities.Permitted, - capability.AMBIENT: spec.Process.Capabilities.Ambient, - } - knownCaps := capability.List() - noCap := capability.Cap(-1) - for capType, capList := range capMap { - for _, capToSet := range capList { - cap := noCap - for _, c := range knownCaps { - if strings.EqualFold("CAP_"+c.String(), capToSet) { - cap = c - break - } - } - if cap == noCap { - return fmt.Errorf("error mapping capability %q to a number", capToSet) - } - caps.Set(capType, cap) - } - for _, capToSet := range keepCaps { - cap := noCap - for _, c := range knownCaps { - if strings.EqualFold("CAP_"+c.String(), capToSet) { - cap = c - break - } - } - if cap == noCap { - return fmt.Errorf("error mapping capability %q to a number", capToSet) - } - if currentCaps.Get(capType, cap) { - caps.Set(capType, cap) - } - } - } - if err = caps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS); err != nil { - return fmt.Errorf("error setting capabilities: %w", err) - } - return nil -} - -// parses the resource limits for ourselves and any processes that -// we'll start into a format that's more in line with the kernel APIs -func parseRlimits(spec *specs.Spec) (map[int]unix.Rlimit, error) { - if spec.Process == nil { - return nil, nil - } - parsed := make(map[int]unix.Rlimit) - for _, limit := range spec.Process.Rlimits { - resource, recognized := rlimitsMap[strings.ToUpper(limit.Type)] - if !recognized { - return nil, fmt.Errorf("error parsing limit type %q", limit.Type) - } - parsed[resource] = unix.Rlimit{Cur: limit.Soft, Max: limit.Hard} - } - return parsed, nil -} - -// setRlimits sets any resource limits that we want to apply to processes that -// we'll start. -func setRlimits(spec *specs.Spec, onlyLower, onlyRaise bool) error { - limits, err := parseRlimits(spec) - if err != nil { - return err - } - for resource, desired := range limits { - var current unix.Rlimit - if err := unix.Getrlimit(resource, ¤t); err != nil { - return fmt.Errorf("error reading %q limit: %w", rlimitsReverseMap[resource], err) - } - if desired.Max > current.Max && onlyLower { - // this would raise a hard limit, and we're only here to lower them - continue - } - if desired.Max < current.Max && onlyRaise { - // this would lower a hard limit, and we're only here to raise them - continue - } - if err := unix.Setrlimit(resource, &desired); err != nil { - return fmt.Errorf("error setting %q limit to soft=%d,hard=%d (was soft=%d,hard=%d): %w", rlimitsReverseMap[resource], desired.Cur, desired.Max, current.Cur, current.Max, err) - } - } - return nil -} - -func makeReadOnly(mntpoint string, flags uintptr) error { - var fs unix.Statfs_t - // Make sure it's read-only. - if err := unix.Statfs(mntpoint, &fs); err != nil { - return fmt.Errorf("error checking if directory %q was bound read-only: %w", mntpoint, err) - } - if fs.Flags&unix.ST_RDONLY == 0 { - if err := unix.Mount(mntpoint, mntpoint, "bind", flags|unix.MS_REMOUNT, ""); err != nil { - return fmt.Errorf("error remounting %s in mount namespace read-only: %w", mntpoint, err) - } - } - return nil -} - -func isDevNull(dev os.FileInfo) bool { - if dev.Mode()&os.ModeCharDevice != 0 { - stat, _ := dev.Sys().(*syscall.Stat_t) - nullStat := syscall.Stat_t{} - if err := syscall.Stat(os.DevNull, &nullStat); err != nil { - logrus.Warnf("unable to stat /dev/null: %v", err) - return false - } - if stat.Rdev == nullStat.Rdev { - return true - } - } - return false -} - -// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a -// callback that will clean up its work. -func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) { - var fs unix.Statfs_t - undoBinds = func() error { - if err2 := unix.Unmount(spec.Root.Path, unix.MNT_DETACH); err2 != nil { - retries := 0 - for (err2 == unix.EBUSY || err2 == unix.EAGAIN) && retries < 50 { - time.Sleep(50 * time.Millisecond) - err2 = unix.Unmount(spec.Root.Path, unix.MNT_DETACH) - retries++ - } - if err2 != nil { - logrus.Warnf("pkg/chroot: error unmounting %q (retried %d times): %v", spec.Root.Path, retries, err2) - if err == nil { - err = err2 - } - } - } - return err - } - - // Now bind mount all of those things to be under the rootfs's location in this - // mount namespace. - commonFlags := uintptr(unix.MS_BIND | unix.MS_REC | unix.MS_PRIVATE) - bindFlags := commonFlags | unix.MS_NODEV - devFlags := commonFlags | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY - procFlags := devFlags | unix.MS_NODEV - sysFlags := devFlags | unix.MS_NODEV - - // Bind /dev read-only. - subDev := filepath.Join(spec.Root.Path, "/dev") - if err := unix.Mount("/dev", subDev, "bind", devFlags, ""); err != nil { - if os.IsNotExist(err) { - err = os.Mkdir(subDev, 0755) - if err == nil { - err = unix.Mount("/dev", subDev, "bind", devFlags, "") - } - } - if err != nil { - return undoBinds, fmt.Errorf("error bind mounting /dev from host into mount namespace: %w", err) - } - } - // Make sure it's read-only. - if err = unix.Statfs(subDev, &fs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", subDev, err) - } - if fs.Flags&unix.ST_RDONLY == 0 { - if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT, ""); err != nil { - return undoBinds, fmt.Errorf("error remounting /dev in mount namespace read-only: %w", err) - } - } - logrus.Debugf("bind mounted %q to %q", "/dev", filepath.Join(spec.Root.Path, "/dev")) - - // Bind /proc read-only. - subProc := filepath.Join(spec.Root.Path, "/proc") - if err := unix.Mount("/proc", subProc, "bind", procFlags, ""); err != nil { - if os.IsNotExist(err) { - err = os.Mkdir(subProc, 0755) - if err == nil { - err = unix.Mount("/proc", subProc, "bind", procFlags, "") - } - } - if err != nil { - return undoBinds, fmt.Errorf("error bind mounting /proc from host into mount namespace: %w", err) - } - } - logrus.Debugf("bind mounted %q to %q", "/proc", filepath.Join(spec.Root.Path, "/proc")) - - // Bind /sys read-only. - subSys := filepath.Join(spec.Root.Path, "/sys") - if err := unix.Mount("/sys", subSys, "bind", sysFlags, ""); err != nil { - if os.IsNotExist(err) { - err = os.Mkdir(subSys, 0755) - if err == nil { - err = unix.Mount("/sys", subSys, "bind", sysFlags, "") - } - } - if err != nil { - return undoBinds, fmt.Errorf("error bind mounting /sys from host into mount namespace: %w", err) - } - } - if err := makeReadOnly(subSys, sysFlags); err != nil { - return undoBinds, err - } - - mnts, _ := mount.GetMounts() - for _, m := range mnts { - if !strings.HasPrefix(m.Mountpoint, "/sys/") && - m.Mountpoint != "/sys" { - continue - } - subSys := filepath.Join(spec.Root.Path, m.Mountpoint) - if err := unix.Mount(m.Mountpoint, subSys, "bind", sysFlags, ""); err != nil { - msg := fmt.Sprintf("could not bind mount %q, skipping: %v", m.Mountpoint, err) - if strings.HasPrefix(m.Mountpoint, "/sys") { - logrus.Infof(msg) - } else { - logrus.Warningf(msg) - } - continue - } - if err := makeReadOnly(subSys, sysFlags); err != nil { - return undoBinds, err - } - } - logrus.Debugf("bind mounted %q to %q", "/sys", filepath.Join(spec.Root.Path, "/sys")) - - // Bind mount in everything we've been asked to mount. - for _, m := range spec.Mounts { - // Skip anything that we just mounted. - switch m.Destination { - case "/dev", "/proc", "/sys": - logrus.Debugf("already bind mounted %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination)) - continue - default: - if strings.HasPrefix(m.Destination, "/dev/") { - continue - } - if strings.HasPrefix(m.Destination, "/proc/") { - continue - } - if strings.HasPrefix(m.Destination, "/sys/") { - continue - } - } - // Skip anything that isn't a bind or tmpfs mount. - if m.Type != "bind" && m.Type != "tmpfs" && m.Type != "overlay" { - logrus.Debugf("skipping mount of type %q on %q", m.Type, m.Destination) - continue - } - // If the target is there, we can just mount it. - var srcinfo os.FileInfo - switch m.Type { - case "bind": - srcinfo, err = os.Stat(m.Source) - if err != nil { - return undoBinds, fmt.Errorf("error examining %q for mounting in mount namespace: %w", m.Source, err) - } - case "overlay": - fallthrough - case "tmpfs": - srcinfo, err = os.Stat("/") - if err != nil { - return undoBinds, fmt.Errorf("error examining / to use as a template for a %s: %w", m.Type, err) - } - } - target := filepath.Join(spec.Root.Path, m.Destination) - // Check if target is a symlink - stat, err := os.Lstat(target) - // If target is a symlink, follow the link and ensure the destination exists - if err == nil && stat != nil && (stat.Mode()&os.ModeSymlink != 0) { - target, err = copier.Eval(spec.Root.Path, m.Destination, copier.EvalOptions{}) - if err != nil { - return nil, fmt.Errorf("evaluating symlink %q: %w", target, err) - } - // Stat the destination of the evaluated symlink - _, err = os.Stat(target) - } - if err != nil { - // If the target can't be stat()ted, check the error. - if !os.IsNotExist(err) { - return undoBinds, fmt.Errorf("error examining %q for mounting in mount namespace: %w", target, err) - } - // The target isn't there yet, so create it. - if srcinfo.IsDir() { - if err = os.MkdirAll(target, 0755); err != nil { - return undoBinds, fmt.Errorf("error creating mountpoint %q in mount namespace: %w", target, err) - } - } else { - if err = os.MkdirAll(filepath.Dir(target), 0755); err != nil { - return undoBinds, fmt.Errorf("error ensuring parent of mountpoint %q (%q) is present in mount namespace: %w", target, filepath.Dir(target), err) - } - var file *os.File - if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0755); err != nil { - return undoBinds, fmt.Errorf("error creating mountpoint %q in mount namespace: %w", target, err) - } - file.Close() - } - } - requestFlags := bindFlags - expectedFlags := uintptr(0) - for _, option := range m.Options { - switch option { - case "nodev": - requestFlags |= unix.MS_NODEV - expectedFlags |= unix.ST_NODEV - case "dev": - requestFlags &= ^uintptr(unix.MS_NODEV) - expectedFlags &= ^uintptr(unix.ST_NODEV) - case "noexec": - requestFlags |= unix.MS_NOEXEC - expectedFlags |= unix.ST_NOEXEC - case "exec": - requestFlags &= ^uintptr(unix.MS_NOEXEC) - expectedFlags &= ^uintptr(unix.ST_NOEXEC) - case "nosuid": - requestFlags |= unix.MS_NOSUID - expectedFlags |= unix.ST_NOSUID - case "suid": - requestFlags &= ^uintptr(unix.MS_NOSUID) - expectedFlags &= ^uintptr(unix.ST_NOSUID) - case "ro": - requestFlags |= unix.MS_RDONLY - expectedFlags |= unix.ST_RDONLY - case "rw": - requestFlags &= ^uintptr(unix.MS_RDONLY) - expectedFlags &= ^uintptr(unix.ST_RDONLY) - } - } - switch m.Type { - case "bind": - // Do the bind mount. - logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination)) - if err := unix.Mount(m.Source, target, "", requestFlags, ""); err != nil { - return undoBinds, fmt.Errorf("error bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err) - } - logrus.Debugf("bind mounted %q to %q", m.Source, target) - case "tmpfs": - // Mount a tmpfs. - if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil { - return undoBinds, fmt.Errorf("error mounting tmpfs to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err) - } - logrus.Debugf("mounted a tmpfs to %q", target) - case "overlay": - // Mount a overlay. - if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil { - return undoBinds, fmt.Errorf("error mounting overlay to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err) - } - logrus.Debugf("mounted a overlay to %q", target) - } - if err = unix.Statfs(target, &fs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", target, err) - } - if uintptr(fs.Flags)&expectedFlags != expectedFlags { - if err := unix.Mount(target, target, "bind", requestFlags|unix.MS_REMOUNT, ""); err != nil { - return undoBinds, fmt.Errorf("error remounting %q in mount namespace with expected flags: %w", target, err) - } - } - } - - // Set up any read-only paths that we need to. If we're running inside - // of a container, some of these locations will already be read-only. - for _, roPath := range spec.Linux.ReadonlyPaths { - r := filepath.Join(spec.Root.Path, roPath) - target, err := filepath.EvalSymlinks(r) - if err != nil { - if os.IsNotExist(err) { - // No target, no problem. - continue - } - return undoBinds, fmt.Errorf("error checking %q for symlinks before marking it read-only: %w", r, err) - } - // Check if the location is already read-only. - var fs unix.Statfs_t - if err = unix.Statfs(target, &fs); err != nil { - if os.IsNotExist(err) { - // No target, no problem. - continue - } - return undoBinds, fmt.Errorf("error checking if directory %q is already read-only: %w", target, err) - } - if fs.Flags&unix.ST_RDONLY != 0 { - continue - } - // Mount the location over itself, so that we can remount it as read-only. - roFlags := uintptr(unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY) - if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REC, ""); err != nil { - if os.IsNotExist(err) { - // No target, no problem. - continue - } - return undoBinds, fmt.Errorf("error bind mounting %q onto itself in preparation for making it read-only: %w", target, err) - } - // Remount the location read-only. - if err = unix.Statfs(target, &fs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", target, err) - } - if fs.Flags&unix.ST_RDONLY == 0 { - if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REMOUNT, ""); err != nil { - return undoBinds, fmt.Errorf("error remounting %q in mount namespace read-only: %w", target, err) - } - } - // Check again. - if err = unix.Statfs(target, &fs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q was remounted read-only: %w", target, err) - } - if fs.Flags&unix.ST_RDONLY == 0 { - return undoBinds, fmt.Errorf("error verifying that %q in mount namespace was remounted read-only: %w", target, err) - } - } - - // Create an empty directory for to use for masking directories. - roEmptyDir := filepath.Join(bundlePath, "empty") - if len(spec.Linux.MaskedPaths) > 0 { - if err := os.Mkdir(roEmptyDir, 0700); err != nil { - return undoBinds, fmt.Errorf("error creating empty directory %q: %w", roEmptyDir, err) - } - } - - // Set up any masked paths that we need to. If we're running inside of - // a container, some of these locations will already be read-only tmpfs - // filesystems or bind mounted to os.DevNull. If we're not running - // inside of a container, and nobody else has done that, we'll do it. - for _, masked := range spec.Linux.MaskedPaths { - t := filepath.Join(spec.Root.Path, masked) - target, err := filepath.EvalSymlinks(t) - if err != nil { - target = t - } - // Get some info about the target. - targetinfo, err := os.Stat(target) - if err != nil { - if os.IsNotExist(err) { - // No target, no problem. - continue - } - return undoBinds, fmt.Errorf("error examining %q for masking in mount namespace: %w", target, err) - } - if targetinfo.IsDir() { - // The target's a directory. Check if it's a read-only filesystem. - var statfs unix.Statfs_t - if err = unix.Statfs(target, &statfs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q is a mountpoint: %w", target, err) - } - isReadOnly := statfs.Flags&unix.MS_RDONLY != 0 - // Check if any of the IDs we're mapping could read it. - var stat unix.Stat_t - if err = unix.Stat(target, &stat); err != nil { - return undoBinds, fmt.Errorf("error checking permissions on directory %q: %w", target, err) - } - isAccessible := false - if stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 { - isAccessible = true - } - if !isAccessible && stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 { - if len(spec.Linux.GIDMappings) > 0 { - for _, mapping := range spec.Linux.GIDMappings { - if stat.Gid >= mapping.ContainerID && stat.Gid < mapping.ContainerID+mapping.Size { - isAccessible = true - break - } - } - } - } - if !isAccessible && stat.Mode&unix.S_IRUSR|unix.S_IXUSR != 0 { - if len(spec.Linux.UIDMappings) > 0 { - for _, mapping := range spec.Linux.UIDMappings { - if stat.Uid >= mapping.ContainerID && stat.Uid < mapping.ContainerID+mapping.Size { - isAccessible = true - break - } - } - } - } - // Check if it's empty. - hasContent := false - directory, err := os.Open(target) - if err != nil { - if !os.IsPermission(err) { - return undoBinds, fmt.Errorf("error opening directory %q: %w", target, err) - } - } else { - names, err := directory.Readdirnames(0) - directory.Close() - if err != nil { - return undoBinds, fmt.Errorf("error reading contents of directory %q: %w", target, err) - } - hasContent = false - for _, name := range names { - switch name { - case ".", "..": - continue - default: - hasContent = true - } - if hasContent { - break - } - } - } - // The target's a directory, so read-only bind mount an empty directory on it. - roFlags := uintptr(syscall.MS_BIND | syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RDONLY) - if !isReadOnly || (hasContent && isAccessible) { - if err = unix.Mount(roEmptyDir, target, "bind", roFlags, ""); err != nil { - return undoBinds, fmt.Errorf("error masking directory %q in mount namespace: %w", target, err) - } - if err = unix.Statfs(target, &fs); err != nil { - return undoBinds, fmt.Errorf("error checking if directory %q was mounted read-only in mount namespace: %w", target, err) - } - if fs.Flags&unix.ST_RDONLY == 0 { - if err = unix.Mount(target, target, "", roFlags|syscall.MS_REMOUNT, ""); err != nil { - return undoBinds, fmt.Errorf("error making sure directory %q in mount namespace is read only: %w", target, err) - } - } - } - } else { - // If the target's is not a directory or os.DevNull, bind mount os.DevNull over it. - if !isDevNull(targetinfo) { - if err = unix.Mount(os.DevNull, target, "", uintptr(syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_PRIVATE), ""); err != nil { - return undoBinds, fmt.Errorf("error masking non-directory %q in mount namespace: %w", target, err) - } - } - } - } - return undoBinds, nil -} - -// setPdeathsig sets a parent-death signal for the process -func setPdeathsig(cmd *exec.Cmd) { - if cmd.SysProcAttr == nil { - cmd.SysProcAttr = &syscall.SysProcAttr{} - } - cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL -} diff --git a/vendor/github.com/containers/buildah/chroot/run_linux.go b/vendor/github.com/containers/buildah/chroot/run_linux.go new file mode 100644 index 000000000..2e2ed1bb7 --- /dev/null +++ b/vendor/github.com/containers/buildah/chroot/run_linux.go @@ -0,0 +1,1475 @@ +//go:build linux +// +build linux + +package chroot + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/signal" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + "time" + "unsafe" + + "github.com/containers/buildah/bind" + "github.com/containers/buildah/copier" + "github.com/containers/buildah/util" + "github.com/containers/storage/pkg/ioutils" + "github.com/containers/storage/pkg/mount" + "github.com/containers/storage/pkg/reexec" + "github.com/containers/storage/pkg/unshare" + "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" + "github.com/syndtr/gocapability/capability" + "golang.org/x/sys/unix" + "golang.org/x/term" +) + +const ( + // runUsingChrootCommand is a command we use as a key for reexec + runUsingChrootCommand = "buildah-chroot-runtime" + // runUsingChrootExec is a command we use as a key for reexec + runUsingChrootExecCommand = "buildah-chroot-exec" +) + +var ( + rlimitsMap = map[string]int{ + "RLIMIT_AS": unix.RLIMIT_AS, + "RLIMIT_CORE": unix.RLIMIT_CORE, + "RLIMIT_CPU": unix.RLIMIT_CPU, + "RLIMIT_DATA": unix.RLIMIT_DATA, + "RLIMIT_FSIZE": unix.RLIMIT_FSIZE, + "RLIMIT_LOCKS": unix.RLIMIT_LOCKS, + "RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK, + "RLIMIT_MSGQUEUE": unix.RLIMIT_MSGQUEUE, + "RLIMIT_NICE": unix.RLIMIT_NICE, + "RLIMIT_NOFILE": unix.RLIMIT_NOFILE, + "RLIMIT_NPROC": unix.RLIMIT_NPROC, + "RLIMIT_RSS": unix.RLIMIT_RSS, + "RLIMIT_RTPRIO": unix.RLIMIT_RTPRIO, + "RLIMIT_RTTIME": unix.RLIMIT_RTTIME, + "RLIMIT_SIGPENDING": unix.RLIMIT_SIGPENDING, + "RLIMIT_STACK": unix.RLIMIT_STACK, + } + rlimitsReverseMap = map[int]string{} +) + +func init() { + reexec.Register(runUsingChrootCommand, runUsingChrootMain) + reexec.Register(runUsingChrootExecCommand, runUsingChrootExecMain) + for limitName, limitNumber := range rlimitsMap { + rlimitsReverseMap[limitNumber] = limitName + } +} + +type runUsingChrootSubprocOptions struct { + Spec *specs.Spec + BundlePath string + UIDMappings []syscall.SysProcIDMap + GIDMappings []syscall.SysProcIDMap +} + +type runUsingChrootExecSubprocOptions struct { + Spec *specs.Spec + BundlePath string +} + +// RunUsingChroot runs a chrooted process, using some of the settings from the +// passed-in spec, and using the specified bundlePath to hold temporary files, +// directories, and mountpoints. +func RunUsingChroot(spec *specs.Spec, bundlePath, homeDir string, stdin io.Reader, stdout, stderr io.Writer) (err error) { + var confwg sync.WaitGroup + var homeFound bool + for _, env := range spec.Process.Env { + if strings.HasPrefix(env, "HOME=") { + homeFound = true + break + } + } + if !homeFound { + spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("HOME=%s", homeDir)) + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Write the runtime configuration, mainly for debugging. + specbytes, err := json.Marshal(spec) + if err != nil { + return err + } + if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0600); err != nil { + return fmt.Errorf("error storing runtime configuration: %w", err) + } + logrus.Debugf("config = %v", string(specbytes)) + + // Default to using stdin/stdout/stderr if we weren't passed objects to use. + if stdin == nil { + stdin = os.Stdin + } + if stdout == nil { + stdout = os.Stdout + } + if stderr == nil { + stderr = os.Stderr + } + + // Create a pipe for passing configuration down to the next process. + preader, pwriter, err := os.Pipe() + if err != nil { + return fmt.Errorf("error creating configuration pipe: %w", err) + } + config, conferr := json.Marshal(runUsingChrootSubprocOptions{ + Spec: spec, + BundlePath: bundlePath, + }) + if conferr != nil { + return fmt.Errorf("error encoding configuration for %q: %w", runUsingChrootCommand, conferr) + } + + // Set our terminal's mode to raw, to pass handling of special + // terminal input to the terminal in the container. + if spec.Process.Terminal && term.IsTerminal(unix.Stdin) { + state, err := term.MakeRaw(unix.Stdin) + if err != nil { + logrus.Warnf("error setting terminal state: %v", err) + } else { + defer func() { + if err = term.Restore(unix.Stdin, state); err != nil { + logrus.Errorf("unable to restore terminal state: %v", err) + } + }() + } + } + + // Raise any resource limits that are higher than they are now, before + // we drop any more privileges. + if err = setRlimits(spec, false, true); err != nil { + return err + } + + // Start the grandparent subprocess. + cmd := unshare.Command(runUsingChrootCommand) + setPdeathsig(cmd.Cmd) + cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr + cmd.Dir = "/" + cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())} + + interrupted := make(chan os.Signal, 100) + cmd.Hook = func(int) error { + signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + go func() { + for receivedSignal := range interrupted { + if err := cmd.Process.Signal(receivedSignal); err != nil { + logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal) + } + } + }() + return nil + } + + logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd) + confwg.Add(1) + go func() { + _, conferr = io.Copy(pwriter, bytes.NewReader(config)) + pwriter.Close() + confwg.Done() + }() + cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...) + err = cmd.Run() + confwg.Wait() + signal.Stop(interrupted) + close(interrupted) + if err == nil { + return conferr + } + return err +} + +// main() for grandparent subprocess. Its main job is to shuttle stdio back +// and forth, managing a pseudo-terminal if we want one, for our child, the +// parent subprocess. +func runUsingChrootMain() { + var options runUsingChrootSubprocOptions + + runtime.LockOSThread() + + // Set logging. + if level := os.Getenv("LOGLEVEL"); level != "" { + if ll, err := strconv.Atoi(level); err == nil { + logrus.SetLevel(logrus.Level(ll)) + } + os.Unsetenv("LOGLEVEL") + } + + // Unpack our configuration. + confPipe := os.NewFile(3, "confpipe") + if confPipe == nil { + fmt.Fprintf(os.Stderr, "error reading options pipe\n") + os.Exit(1) + } + defer confPipe.Close() + if err := json.NewDecoder(confPipe).Decode(&options); err != nil { + fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err) + os.Exit(1) + } + + if options.Spec == nil || options.Spec.Process == nil { + fmt.Fprintf(os.Stderr, "invalid options spec in runUsingChrootMain\n") + os.Exit(1) + } + + // Prepare to shuttle stdio back and forth. + rootUID32, rootGID32, err := util.GetHostRootIDs(options.Spec) + if err != nil { + logrus.Errorf("error determining ownership for container stdio") + os.Exit(1) + } + rootUID := int(rootUID32) + rootGID := int(rootGID32) + relays := make(map[int]int) + closeOnceRunning := []*os.File{} + var ctty *os.File + var stdin io.Reader + var stdinCopy io.WriteCloser + var stdout io.Writer + var stderr io.Writer + fdDesc := make(map[int]string) + if options.Spec.Process.Terminal { + // Create a pseudo-terminal -- open a copy of the master side. + ptyMasterFd, err := unix.Open("/dev/ptmx", os.O_RDWR, 0600) + if err != nil { + logrus.Errorf("error opening PTY master using /dev/ptmx: %v", err) + os.Exit(1) + } + // Set the kernel's lock to "unlocked". + locked := 0 + if result, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&locked))); int(result) == -1 { + logrus.Errorf("error unlocking PTY descriptor: %v", err) + os.Exit(1) + } + // Get a handle for the other end. + ptyFd, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCGPTPEER, unix.O_RDWR|unix.O_NOCTTY) + if int(ptyFd) == -1 { + if errno, isErrno := err.(syscall.Errno); !isErrno || (errno != syscall.EINVAL && errno != syscall.ENOTTY) { + logrus.Errorf("error getting PTY descriptor: %v", err) + os.Exit(1) + } + // EINVAL means the kernel's too old to understand TIOCGPTPEER. Try TIOCGPTN. + ptyN, err := unix.IoctlGetInt(ptyMasterFd, unix.TIOCGPTN) + if err != nil { + logrus.Errorf("error getting PTY number: %v", err) + os.Exit(1) + } + ptyName := fmt.Sprintf("/dev/pts/%d", ptyN) + fd, err := unix.Open(ptyName, unix.O_RDWR|unix.O_NOCTTY, 0620) + if err != nil { + logrus.Errorf("error opening PTY %q: %v", ptyName, err) + os.Exit(1) + } + ptyFd = uintptr(fd) + } + // Make notes about what's going where. + relays[ptyMasterFd] = unix.Stdout + relays[unix.Stdin] = ptyMasterFd + fdDesc[ptyMasterFd] = "container terminal" + fdDesc[unix.Stdin] = "stdin" + fdDesc[unix.Stdout] = "stdout" + winsize := &unix.Winsize{} + // Set the pseudoterminal's size to the configured size, or our own. + if options.Spec.Process.ConsoleSize != nil { + // Use configured sizes. + winsize.Row = uint16(options.Spec.Process.ConsoleSize.Height) + winsize.Col = uint16(options.Spec.Process.ConsoleSize.Width) + } else { + if term.IsTerminal(unix.Stdin) { + // Use the size of our terminal. + winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ) + if err != nil { + logrus.Debugf("error reading current terminal's size") + winsize.Row = 0 + winsize.Col = 0 + } + } + } + if winsize.Row != 0 && winsize.Col != 0 { + if err = unix.IoctlSetWinsize(int(ptyFd), unix.TIOCSWINSZ, winsize); err != nil { + logrus.Warnf("error setting terminal size for pty") + } + // FIXME - if we're connected to a terminal, we should + // be passing the updated terminal size down when we + // receive a SIGWINCH. + } + // Open an *os.File object that we can pass to our child. + ctty = os.NewFile(ptyFd, "/dev/tty") + // Set ownership for the PTY. + if err = ctty.Chown(rootUID, rootGID); err != nil { + var cttyInfo unix.Stat_t + err2 := unix.Fstat(int(ptyFd), &cttyInfo) + from := "" + op := "setting" + if err2 == nil { + op = "changing" + from = fmt.Sprintf("from %d/%d ", cttyInfo.Uid, cttyInfo.Gid) + } + logrus.Warnf("error %s ownership of container PTY %sto %d/%d: %v", op, from, rootUID, rootGID, err) + } + // Set permissions on the PTY. + if err = ctty.Chmod(0620); err != nil { + logrus.Errorf("error setting permissions of container PTY: %v", err) + os.Exit(1) + } + // Make a note that our child (the parent subprocess) should + // have the PTY connected to its stdio, and that we should + // close it once it's running. + stdin = ctty + stdout = ctty + stderr = ctty + closeOnceRunning = append(closeOnceRunning, ctty) + } else { + // Create pipes for stdio. + stdinRead, stdinWrite, err := os.Pipe() + if err != nil { + logrus.Errorf("error opening pipe for stdin: %v", err) + } + stdoutRead, stdoutWrite, err := os.Pipe() + if err != nil { + logrus.Errorf("error opening pipe for stdout: %v", err) + } + stderrRead, stderrWrite, err := os.Pipe() + if err != nil { + logrus.Errorf("error opening pipe for stderr: %v", err) + } + // Make notes about what's going where. + relays[unix.Stdin] = int(stdinWrite.Fd()) + relays[int(stdoutRead.Fd())] = unix.Stdout + relays[int(stderrRead.Fd())] = unix.Stderr + fdDesc[int(stdinWrite.Fd())] = "container stdin pipe" + fdDesc[int(stdoutRead.Fd())] = "container stdout pipe" + fdDesc[int(stderrRead.Fd())] = "container stderr pipe" + fdDesc[unix.Stdin] = "stdin" + fdDesc[unix.Stdout] = "stdout" + fdDesc[unix.Stderr] = "stderr" + // Set ownership for the pipes. + if err = stdinRead.Chown(rootUID, rootGID); err != nil { + logrus.Errorf("error setting ownership of container stdin pipe: %v", err) + os.Exit(1) + } + if err = stdoutWrite.Chown(rootUID, rootGID); err != nil { + logrus.Errorf("error setting ownership of container stdout pipe: %v", err) + os.Exit(1) + } + if err = stderrWrite.Chown(rootUID, rootGID); err != nil { + logrus.Errorf("error setting ownership of container stderr pipe: %v", err) + os.Exit(1) + } + // Make a note that our child (the parent subprocess) should + // have the pipes connected to its stdio, and that we should + // close its ends of them once it's running. + stdin = stdinRead + stdout = stdoutWrite + stderr = stderrWrite + closeOnceRunning = append(closeOnceRunning, stdinRead, stdoutWrite, stderrWrite) + stdinCopy = stdinWrite + defer stdoutRead.Close() + defer stderrRead.Close() + } + for readFd, writeFd := range relays { + if err := unix.SetNonblock(readFd, true); err != nil { + logrus.Errorf("error setting descriptor %d (%s) non-blocking: %v", readFd, fdDesc[readFd], err) + return + } + if err := unix.SetNonblock(writeFd, false); err != nil { + logrus.Errorf("error setting descriptor %d (%s) blocking: %v", relays[writeFd], fdDesc[writeFd], err) + return + } + } + if err := unix.SetNonblock(relays[unix.Stdin], true); err != nil { + logrus.Errorf("error setting %d to nonblocking: %v", relays[unix.Stdin], err) + } + go func() { + buffers := make(map[int]*bytes.Buffer) + for _, writeFd := range relays { + buffers[writeFd] = new(bytes.Buffer) + } + pollTimeout := -1 + stdinClose := false + for len(relays) > 0 { + fds := make([]unix.PollFd, 0, len(relays)) + for fd := range relays { + fds = append(fds, unix.PollFd{Fd: int32(fd), Events: unix.POLLIN | unix.POLLHUP}) + } + _, err := unix.Poll(fds, pollTimeout) + if !util.LogIfNotRetryable(err, fmt.Sprintf("poll: %v", err)) { + return + } + removeFds := make(map[int]struct{}) + for _, rfd := range fds { + if rfd.Revents&unix.POLLHUP == unix.POLLHUP { + removeFds[int(rfd.Fd)] = struct{}{} + } + if rfd.Revents&unix.POLLNVAL == unix.POLLNVAL { + logrus.Debugf("error polling descriptor %s: closed?", fdDesc[int(rfd.Fd)]) + removeFds[int(rfd.Fd)] = struct{}{} + } + if rfd.Revents&unix.POLLIN == 0 { + if stdinClose && stdinCopy == nil { + continue + } + continue + } + b := make([]byte, 8192) + nread, err := unix.Read(int(rfd.Fd), b) + util.LogIfNotRetryable(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err)) + if nread > 0 { + if wfd, ok := relays[int(rfd.Fd)]; ok { + nwritten, err := buffers[wfd].Write(b[:nread]) + if err != nil { + logrus.Debugf("buffer: %v", err) + continue + } + if nwritten != nread { + logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nread, nwritten) + continue + } + } + // If this is the last of the data we'll be able to read + // from this descriptor, read as much as there is to read. + for rfd.Revents&unix.POLLHUP == unix.POLLHUP { + nr, err := unix.Read(int(rfd.Fd), b) + util.LogIfUnexpectedWhileDraining(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err)) + if nr <= 0 { + break + } + if wfd, ok := relays[int(rfd.Fd)]; ok { + nwritten, err := buffers[wfd].Write(b[:nr]) + if err != nil { + logrus.Debugf("buffer: %v", err) + break + } + if nwritten != nr { + logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nr, nwritten) + break + } + } + } + } + if nread == 0 { + removeFds[int(rfd.Fd)] = struct{}{} + } + } + pollTimeout = -1 + for wfd, buffer := range buffers { + if buffer.Len() > 0 { + nwritten, err := unix.Write(wfd, buffer.Bytes()) + util.LogIfNotRetryable(err, fmt.Sprintf("write %s: %v", fdDesc[wfd], err)) + if nwritten >= 0 { + _ = buffer.Next(nwritten) + } + } + if buffer.Len() > 0 { + pollTimeout = 100 + } + if wfd == relays[unix.Stdin] && stdinClose && buffer.Len() == 0 { + stdinCopy.Close() + delete(relays, unix.Stdin) + } + } + for rfd := range removeFds { + if rfd == unix.Stdin { + buffer, found := buffers[relays[unix.Stdin]] + if found && buffer.Len() > 0 { + stdinClose = true + continue + } + } + if !options.Spec.Process.Terminal && rfd == unix.Stdin { + stdinCopy.Close() + } + delete(relays, rfd) + } + } + }() + + // Set up mounts and namespaces, and run the parent subprocess. + status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, closeOnceRunning) + if err != nil { + fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err) + os.Exit(1) + } + + // Pass the process's exit status back to the caller by exiting with the same status. + if status.Exited() { + if status.ExitStatus() != 0 { + fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", status.ExitStatus()) + } + os.Exit(status.ExitStatus()) + } else if status.Signaled() { + fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", status.Signal()) + os.Exit(1) + } +} + +// runUsingChroot, still in the grandparent process, sets up various bind +// mounts and then runs the parent process in its own user namespace with the +// necessary ID mappings. +func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io.Reader, stdout, stderr io.Writer, closeOnceRunning []*os.File) (wstatus unix.WaitStatus, err error) { + var confwg sync.WaitGroup + + // Create a new mount namespace for ourselves and bind mount everything to a new location. + undoIntermediates, err := bind.SetupIntermediateMountNamespace(spec, bundlePath) + if err != nil { + return 1, err + } + defer func() { + if undoErr := undoIntermediates(); undoErr != nil { + logrus.Debugf("error cleaning up intermediate mount NS: %v", err) + } + }() + + // Bind mount in our filesystems. + undoChroots, err := setupChrootBindMounts(spec, bundlePath) + if err != nil { + return 1, err + } + defer func() { + if undoErr := undoChroots(); undoErr != nil { + logrus.Debugf("error cleaning up intermediate chroot bind mounts: %v", err) + } + }() + + // Create a pipe for passing configuration down to the next process. + preader, pwriter, err := os.Pipe() + if err != nil { + return 1, fmt.Errorf("error creating configuration pipe: %w", err) + } + config, conferr := json.Marshal(runUsingChrootExecSubprocOptions{ + Spec: spec, + BundlePath: bundlePath, + }) + if conferr != nil { + fmt.Fprintf(os.Stderr, "error re-encoding configuration for %q", runUsingChrootExecCommand) + os.Exit(1) + } + + // Apologize for the namespace configuration that we're about to ignore. + logNamespaceDiagnostics(spec) + + // If we have configured ID mappings, set them here so that they can apply to the child. + hostUidmap, hostGidmap, err := unshare.GetHostIDMappings("") + if err != nil { + return 1, err + } + uidmap, gidmap := spec.Linux.UIDMappings, spec.Linux.GIDMappings + if len(uidmap) == 0 { + // No UID mappings are configured for the container. Borrow our parent's mappings. + uidmap = append([]specs.LinuxIDMapping{}, hostUidmap...) + for i := range uidmap { + uidmap[i].HostID = uidmap[i].ContainerID + } + } + if len(gidmap) == 0 { + // No GID mappings are configured for the container. Borrow our parent's mappings. + gidmap = append([]specs.LinuxIDMapping{}, hostGidmap...) + for i := range gidmap { + gidmap[i].HostID = gidmap[i].ContainerID + } + } + + // Start the parent subprocess. + cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...) + setPdeathsig(cmd.Cmd) + cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr + cmd.Dir = "/" + cmd.Env = []string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())} + cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS + requestedUserNS := false + for _, ns := range spec.Linux.Namespaces { + if ns.Type == specs.UserNamespace { + requestedUserNS = true + } + } + if len(spec.Linux.UIDMappings) > 0 || len(spec.Linux.GIDMappings) > 0 || requestedUserNS { + cmd.UnshareFlags = cmd.UnshareFlags | syscall.CLONE_NEWUSER + cmd.UidMappings = uidmap + cmd.GidMappings = gidmap + cmd.GidMappingsEnableSetgroups = true + } + if ctty != nil { + cmd.Setsid = true + cmd.Ctty = ctty + } + cmd.OOMScoreAdj = spec.Process.OOMScoreAdj + cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...) + interrupted := make(chan os.Signal, 100) + cmd.Hook = func(int) error { + for _, f := range closeOnceRunning { + f.Close() + } + signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + go func() { + for receivedSignal := range interrupted { + if err := cmd.Process.Signal(receivedSignal); err != nil { + logrus.Infof("%v while attempting to forward %v to child process", err, receivedSignal) + } + } + }() + return nil + } + + logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd) + confwg.Add(1) + go func() { + _, conferr = io.Copy(pwriter, bytes.NewReader(config)) + pwriter.Close() + confwg.Done() + }() + err = cmd.Run() + confwg.Wait() + signal.Stop(interrupted) + close(interrupted) + if err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { + if waitStatus.Exited() { + if waitStatus.ExitStatus() != 0 { + fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus()) + } + os.Exit(waitStatus.ExitStatus()) + } else if waitStatus.Signaled() { + fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal()) + os.Exit(1) + } + } + } + fmt.Fprintf(os.Stderr, "process exited with error: %v", err) + os.Exit(1) + } + + return 0, nil +} + +// main() for parent subprocess. Its main job is to try to make our +// environment look like the one described by the runtime configuration blob, +// and then launch the intended command as a child. +func runUsingChrootExecMain() { + args := os.Args[1:] + var options runUsingChrootExecSubprocOptions + var err error + + runtime.LockOSThread() + + // Set logging. + if level := os.Getenv("LOGLEVEL"); level != "" { + if ll, err := strconv.Atoi(level); err == nil { + logrus.SetLevel(logrus.Level(ll)) + } + os.Unsetenv("LOGLEVEL") + } + + // Unpack our configuration. + confPipe := os.NewFile(3, "confpipe") + if confPipe == nil { + fmt.Fprintf(os.Stderr, "error reading options pipe\n") + os.Exit(1) + } + defer confPipe.Close() + if err := json.NewDecoder(confPipe).Decode(&options); err != nil { + fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err) + os.Exit(1) + } + + // Set the hostname. We're already in a distinct UTS namespace and are admins in the user + // namespace which created it, so we shouldn't get a permissions error, but seccomp policy + // might deny our attempt to call sethostname() anyway, so log a debug message for that. + if options.Spec == nil || options.Spec.Process == nil { + fmt.Fprintf(os.Stderr, "invalid options spec passed in\n") + os.Exit(1) + } + + if options.Spec.Hostname != "" { + if err := unix.Sethostname([]byte(options.Spec.Hostname)); err != nil { + logrus.Debugf("failed to set hostname %q for process: %v", options.Spec.Hostname, err) + } + } + + // Try to chroot into the root. Do this before we potentially block the syscall via the + // seccomp profile. + var oldst, newst unix.Stat_t + if err := unix.Stat(options.Spec.Root.Path, &oldst); err != nil { + fmt.Fprintf(os.Stderr, "error stat()ing intended root directory %q: %v\n", options.Spec.Root.Path, err) + os.Exit(1) + } + if err := unix.Chdir(options.Spec.Root.Path); err != nil { + fmt.Fprintf(os.Stderr, "error chdir()ing to intended root directory %q: %v\n", options.Spec.Root.Path, err) + os.Exit(1) + } + if err := unix.Chroot(options.Spec.Root.Path); err != nil { + fmt.Fprintf(os.Stderr, "error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err) + os.Exit(1) + } + if err := unix.Stat("/", &newst); err != nil { + fmt.Fprintf(os.Stderr, "error stat()ing current root directory: %v\n", err) + os.Exit(1) + } + if oldst.Dev != newst.Dev || oldst.Ino != newst.Ino { + fmt.Fprintf(os.Stderr, "unknown error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err) + os.Exit(1) + } + logrus.Debugf("chrooted into %q", options.Spec.Root.Path) + + // not doing because it's still shared: creating devices + // not doing because it's not applicable: setting annotations + // not doing because it's still shared: setting sysctl settings + // not doing because cgroupfs is read only: configuring control groups + // -> this means we can use the freezer to make sure there aren't any lingering processes + // -> this means we ignore cgroups-based controls + // not doing because we don't set any in the config: running hooks + // not doing because we don't set it in the config: setting rootfs read-only + // not doing because we don't set it in the config: setting rootfs propagation + logrus.Debugf("setting apparmor profile") + if err = setApparmorProfile(options.Spec); err != nil { + fmt.Fprintf(os.Stderr, "error setting apparmor profile for process: %v\n", err) + os.Exit(1) + } + if err = setSelinuxLabel(options.Spec); err != nil { + fmt.Fprintf(os.Stderr, "error setting SELinux label for process: %v\n", err) + os.Exit(1) + } + + logrus.Debugf("setting resource limits") + if err = setRlimits(options.Spec, false, false); err != nil { + fmt.Fprintf(os.Stderr, "error setting process resource limits for process: %v\n", err) + os.Exit(1) + } + + // Try to change to the directory. + cwd := options.Spec.Process.Cwd + if !filepath.IsAbs(cwd) { + cwd = "/" + cwd + } + cwd = filepath.Clean(cwd) + if err := unix.Chdir("/"); err != nil { + fmt.Fprintf(os.Stderr, "error chdir()ing into new root directory %q: %v\n", options.Spec.Root.Path, err) + os.Exit(1) + } + if err := unix.Chdir(cwd); err != nil { + fmt.Fprintf(os.Stderr, "error chdir()ing into directory %q under root %q: %v\n", cwd, options.Spec.Root.Path, err) + os.Exit(1) + } + logrus.Debugf("changed working directory to %q", cwd) + + // Drop privileges. + user := options.Spec.Process.User + if len(user.AdditionalGids) > 0 { + gids := make([]int, len(user.AdditionalGids)) + for i := range user.AdditionalGids { + gids[i] = int(user.AdditionalGids[i]) + } + logrus.Debugf("setting supplemental groups") + if err = syscall.Setgroups(gids); err != nil { + fmt.Fprintf(os.Stderr, "error setting supplemental groups list: %v", err) + os.Exit(1) + } + } else { + setgroups, _ := ioutil.ReadFile("/proc/self/setgroups") + if strings.Trim(string(setgroups), "\n") != "deny" { + logrus.Debugf("clearing supplemental groups") + if err = syscall.Setgroups([]int{}); err != nil { + fmt.Fprintf(os.Stderr, "error clearing supplemental groups list: %v", err) + os.Exit(1) + } + } + } + + logrus.Debugf("setting gid") + if err = syscall.Setresgid(int(user.GID), int(user.GID), int(user.GID)); err != nil { + fmt.Fprintf(os.Stderr, "error setting GID: %v", err) + os.Exit(1) + } + + if err = setSeccomp(options.Spec); err != nil { + fmt.Fprintf(os.Stderr, "error setting seccomp filter for process: %v\n", err) + os.Exit(1) + } + + logrus.Debugf("setting capabilities") + var keepCaps []string + if user.UID != 0 { + keepCaps = []string{"CAP_SETUID"} + } + if err := setCapabilities(options.Spec, keepCaps...); err != nil { + fmt.Fprintf(os.Stderr, "error setting capabilities for process: %v\n", err) + os.Exit(1) + } + + logrus.Debugf("setting uid") + if err = syscall.Setresuid(int(user.UID), int(user.UID), int(user.UID)); err != nil { + fmt.Fprintf(os.Stderr, "error setting UID: %v", err) + os.Exit(1) + } + + // Actually run the specified command. + cmd := exec.Command(args[0], args[1:]...) + setPdeathsig(cmd) + cmd.Env = options.Spec.Process.Env + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + cmd.Dir = cwd + logrus.Debugf("Running %#v (PATH = %q)", cmd, os.Getenv("PATH")) + interrupted := make(chan os.Signal, 100) + if err = cmd.Start(); err != nil { + fmt.Fprintf(os.Stderr, "process failed to start with error: %v", err) + } + go func() { + for range interrupted { + if err := cmd.Process.Signal(syscall.SIGKILL); err != nil { + logrus.Infof("%v while attempting to send SIGKILL to child process", err) + } + } + }() + signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + err = cmd.Wait() + signal.Stop(interrupted) + close(interrupted) + if err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { + if waitStatus.Exited() { + if waitStatus.ExitStatus() != 0 { + fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus()) + } + os.Exit(waitStatus.ExitStatus()) + } else if waitStatus.Signaled() { + fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal()) + os.Exit(1) + } + } + } + fmt.Fprintf(os.Stderr, "process exited with error: %v", err) + os.Exit(1) + } +} + +// logNamespaceDiagnostics knows which namespaces we want to create. +// Output debug messages when that differs from what we're being asked to do. +func logNamespaceDiagnostics(spec *specs.Spec) { + sawMountNS := false + sawUTSNS := false + for _, ns := range spec.Linux.Namespaces { + switch ns.Type { + case specs.CgroupNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join cgroup namespace, sorry about that") + } else { + logrus.Debugf("unable to create cgroup namespace, sorry about that") + } + case specs.IPCNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join IPC namespace, sorry about that") + } else { + logrus.Debugf("unable to create IPC namespace, sorry about that") + } + case specs.MountNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join mount namespace %q, creating a new one", ns.Path) + } + sawMountNS = true + case specs.NetworkNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join network namespace, sorry about that") + } else { + logrus.Debugf("unable to create network namespace, sorry about that") + } + case specs.PIDNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join PID namespace, sorry about that") + } else { + logrus.Debugf("unable to create PID namespace, sorry about that") + } + case specs.UserNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join user namespace, sorry about that") + } + case specs.UTSNamespace: + if ns.Path != "" { + logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path) + } + sawUTSNS = true + } + } + if !sawMountNS { + logrus.Debugf("mount namespace not requested, but creating a new one anyway") + } + if !sawUTSNS { + logrus.Debugf("UTS namespace not requested, but creating a new one anyway") + } +} + +// setApparmorProfile sets the apparmor profile for ourselves, and hopefully any child processes that we'll start. +func setApparmorProfile(spec *specs.Spec) error { + if !apparmor.IsEnabled() || spec.Process.ApparmorProfile == "" { + return nil + } + if err := apparmor.ApplyProfile(spec.Process.ApparmorProfile); err != nil { + return fmt.Errorf("error setting apparmor profile to %q: %w", spec.Process.ApparmorProfile, err) + } + return nil +} + +// setCapabilities sets capabilities for ourselves, to be more or less inherited by any processes that we'll start. +func setCapabilities(spec *specs.Spec, keepCaps ...string) error { + currentCaps, err := capability.NewPid2(0) + if err != nil { + return fmt.Errorf("error reading capabilities of current process: %w", err) + } + if err := currentCaps.Load(); err != nil { + return fmt.Errorf("error loading capabilities: %w", err) + } + caps, err := capability.NewPid2(0) + if err != nil { + return fmt.Errorf("error reading capabilities of current process: %w", err) + } + capMap := map[capability.CapType][]string{ + capability.BOUNDING: spec.Process.Capabilities.Bounding, + capability.EFFECTIVE: spec.Process.Capabilities.Effective, + capability.INHERITABLE: []string{}, + capability.PERMITTED: spec.Process.Capabilities.Permitted, + capability.AMBIENT: spec.Process.Capabilities.Ambient, + } + knownCaps := capability.List() + noCap := capability.Cap(-1) + for capType, capList := range capMap { + for _, capToSet := range capList { + cap := noCap + for _, c := range knownCaps { + if strings.EqualFold("CAP_"+c.String(), capToSet) { + cap = c + break + } + } + if cap == noCap { + return fmt.Errorf("error mapping capability %q to a number", capToSet) + } + caps.Set(capType, cap) + } + for _, capToSet := range keepCaps { + cap := noCap + for _, c := range knownCaps { + if strings.EqualFold("CAP_"+c.String(), capToSet) { + cap = c + break + } + } + if cap == noCap { + return fmt.Errorf("error mapping capability %q to a number", capToSet) + } + if currentCaps.Get(capType, cap) { + caps.Set(capType, cap) + } + } + } + if err = caps.Apply(capability.CAPS | capability.BOUNDS | capability.AMBS); err != nil { + return fmt.Errorf("error setting capabilities: %w", err) + } + return nil +} + +// parses the resource limits for ourselves and any processes that +// we'll start into a format that's more in line with the kernel APIs +func parseRlimits(spec *specs.Spec) (map[int]unix.Rlimit, error) { + if spec.Process == nil { + return nil, nil + } + parsed := make(map[int]unix.Rlimit) + for _, limit := range spec.Process.Rlimits { + resource, recognized := rlimitsMap[strings.ToUpper(limit.Type)] + if !recognized { + return nil, fmt.Errorf("error parsing limit type %q", limit.Type) + } + parsed[resource] = unix.Rlimit{Cur: limit.Soft, Max: limit.Hard} + } + return parsed, nil +} + +// setRlimits sets any resource limits that we want to apply to processes that +// we'll start. +func setRlimits(spec *specs.Spec, onlyLower, onlyRaise bool) error { + limits, err := parseRlimits(spec) + if err != nil { + return err + } + for resource, desired := range limits { + var current unix.Rlimit + if err := unix.Getrlimit(resource, ¤t); err != nil { + return fmt.Errorf("error reading %q limit: %w", rlimitsReverseMap[resource], err) + } + if desired.Max > current.Max && onlyLower { + // this would raise a hard limit, and we're only here to lower them + continue + } + if desired.Max < current.Max && onlyRaise { + // this would lower a hard limit, and we're only here to raise them + continue + } + if err := unix.Setrlimit(resource, &desired); err != nil { + return fmt.Errorf("error setting %q limit to soft=%d,hard=%d (was soft=%d,hard=%d): %w", rlimitsReverseMap[resource], desired.Cur, desired.Max, current.Cur, current.Max, err) + } + } + return nil +} + +func makeReadOnly(mntpoint string, flags uintptr) error { + var fs unix.Statfs_t + // Make sure it's read-only. + if err := unix.Statfs(mntpoint, &fs); err != nil { + return fmt.Errorf("error checking if directory %q was bound read-only: %w", mntpoint, err) + } + if fs.Flags&unix.ST_RDONLY == 0 { + if err := unix.Mount(mntpoint, mntpoint, "bind", flags|unix.MS_REMOUNT, ""); err != nil { + return fmt.Errorf("error remounting %s in mount namespace read-only: %w", mntpoint, err) + } + } + return nil +} + +func isDevNull(dev os.FileInfo) bool { + if dev.Mode()&os.ModeCharDevice != 0 { + stat, _ := dev.Sys().(*syscall.Stat_t) + nullStat := syscall.Stat_t{} + if err := syscall.Stat(os.DevNull, &nullStat); err != nil { + logrus.Warnf("unable to stat /dev/null: %v", err) + return false + } + if stat.Rdev == nullStat.Rdev { + return true + } + } + return false +} + +// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a +// callback that will clean up its work. +func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) { + var fs unix.Statfs_t + undoBinds = func() error { + if err2 := unix.Unmount(spec.Root.Path, unix.MNT_DETACH); err2 != nil { + retries := 0 + for (err2 == unix.EBUSY || err2 == unix.EAGAIN) && retries < 50 { + time.Sleep(50 * time.Millisecond) + err2 = unix.Unmount(spec.Root.Path, unix.MNT_DETACH) + retries++ + } + if err2 != nil { + logrus.Warnf("pkg/chroot: error unmounting %q (retried %d times): %v", spec.Root.Path, retries, err2) + if err == nil { + err = err2 + } + } + } + return err + } + + // Now bind mount all of those things to be under the rootfs's location in this + // mount namespace. + commonFlags := uintptr(unix.MS_BIND | unix.MS_REC | unix.MS_PRIVATE) + bindFlags := commonFlags | unix.MS_NODEV + devFlags := commonFlags | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY + procFlags := devFlags | unix.MS_NODEV + sysFlags := devFlags | unix.MS_NODEV + + // Bind /dev read-only. + subDev := filepath.Join(spec.Root.Path, "/dev") + if err := unix.Mount("/dev", subDev, "bind", devFlags, ""); err != nil { + if errors.Is(err, os.ErrNotExist) { + err = os.Mkdir(subDev, 0755) + if err == nil { + err = unix.Mount("/dev", subDev, "bind", devFlags, "") + } + } + if err != nil { + return undoBinds, fmt.Errorf("error bind mounting /dev from host into mount namespace: %w", err) + } + } + // Make sure it's read-only. + if err = unix.Statfs(subDev, &fs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", subDev, err) + } + if fs.Flags&unix.ST_RDONLY == 0 { + if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT, ""); err != nil { + return undoBinds, fmt.Errorf("error remounting /dev in mount namespace read-only: %w", err) + } + } + logrus.Debugf("bind mounted %q to %q", "/dev", filepath.Join(spec.Root.Path, "/dev")) + + // Bind /proc read-only. + subProc := filepath.Join(spec.Root.Path, "/proc") + if err := unix.Mount("/proc", subProc, "bind", procFlags, ""); err != nil { + if errors.Is(err, os.ErrNotExist) { + err = os.Mkdir(subProc, 0755) + if err == nil { + err = unix.Mount("/proc", subProc, "bind", procFlags, "") + } + } + if err != nil { + return undoBinds, fmt.Errorf("error bind mounting /proc from host into mount namespace: %w", err) + } + } + logrus.Debugf("bind mounted %q to %q", "/proc", filepath.Join(spec.Root.Path, "/proc")) + + // Bind /sys read-only. + subSys := filepath.Join(spec.Root.Path, "/sys") + if err := unix.Mount("/sys", subSys, "bind", sysFlags, ""); err != nil { + if errors.Is(err, os.ErrNotExist) { + err = os.Mkdir(subSys, 0755) + if err == nil { + err = unix.Mount("/sys", subSys, "bind", sysFlags, "") + } + } + if err != nil { + return undoBinds, fmt.Errorf("error bind mounting /sys from host into mount namespace: %w", err) + } + } + if err := makeReadOnly(subSys, sysFlags); err != nil { + return undoBinds, err + } + + mnts, _ := mount.GetMounts() + for _, m := range mnts { + if !strings.HasPrefix(m.Mountpoint, "/sys/") && + m.Mountpoint != "/sys" { + continue + } + subSys := filepath.Join(spec.Root.Path, m.Mountpoint) + if err := unix.Mount(m.Mountpoint, subSys, "bind", sysFlags, ""); err != nil { + msg := fmt.Sprintf("could not bind mount %q, skipping: %v", m.Mountpoint, err) + if strings.HasPrefix(m.Mountpoint, "/sys") { + logrus.Infof(msg) + } else { + logrus.Warningf(msg) + } + continue + } + if err := makeReadOnly(subSys, sysFlags); err != nil { + return undoBinds, err + } + } + logrus.Debugf("bind mounted %q to %q", "/sys", filepath.Join(spec.Root.Path, "/sys")) + + // Bind mount in everything we've been asked to mount. + for _, m := range spec.Mounts { + // Skip anything that we just mounted. + switch m.Destination { + case "/dev", "/proc", "/sys": + logrus.Debugf("already bind mounted %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination)) + continue + default: + if strings.HasPrefix(m.Destination, "/dev/") { + continue + } + if strings.HasPrefix(m.Destination, "/proc/") { + continue + } + if strings.HasPrefix(m.Destination, "/sys/") { + continue + } + } + // Skip anything that isn't a bind or tmpfs mount. + if m.Type != "bind" && m.Type != "tmpfs" && m.Type != "overlay" { + logrus.Debugf("skipping mount of type %q on %q", m.Type, m.Destination) + continue + } + // If the target is there, we can just mount it. + var srcinfo os.FileInfo + switch m.Type { + case "bind": + srcinfo, err = os.Stat(m.Source) + if err != nil { + return undoBinds, fmt.Errorf("error examining %q for mounting in mount namespace: %w", m.Source, err) + } + case "overlay": + fallthrough + case "tmpfs": + srcinfo, err = os.Stat("/") + if err != nil { + return undoBinds, fmt.Errorf("error examining / to use as a template for a %s: %w", m.Type, err) + } + } + target := filepath.Join(spec.Root.Path, m.Destination) + // Check if target is a symlink + stat, err := os.Lstat(target) + // If target is a symlink, follow the link and ensure the destination exists + if err == nil && stat != nil && (stat.Mode()&os.ModeSymlink != 0) { + target, err = copier.Eval(spec.Root.Path, m.Destination, copier.EvalOptions{}) + if err != nil { + return nil, fmt.Errorf("evaluating symlink %q: %w", target, err) + } + // Stat the destination of the evaluated symlink + _, err = os.Stat(target) + } + if err != nil { + // If the target can't be stat()ted, check the error. + if !errors.Is(err, os.ErrNotExist) { + return undoBinds, fmt.Errorf("error examining %q for mounting in mount namespace: %w", target, err) + } + // The target isn't there yet, so create it. + if srcinfo.IsDir() { + if err = os.MkdirAll(target, 0755); err != nil { + return undoBinds, fmt.Errorf("error creating mountpoint %q in mount namespace: %w", target, err) + } + } else { + if err = os.MkdirAll(filepath.Dir(target), 0755); err != nil { + return undoBinds, fmt.Errorf("error ensuring parent of mountpoint %q (%q) is present in mount namespace: %w", target, filepath.Dir(target), err) + } + var file *os.File + if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0755); err != nil { + return undoBinds, fmt.Errorf("error creating mountpoint %q in mount namespace: %w", target, err) + } + file.Close() + } + } + requestFlags := bindFlags + expectedFlags := uintptr(0) + for _, option := range m.Options { + switch option { + case "nodev": + requestFlags |= unix.MS_NODEV + expectedFlags |= unix.ST_NODEV + case "dev": + requestFlags &= ^uintptr(unix.MS_NODEV) + expectedFlags &= ^uintptr(unix.ST_NODEV) + case "noexec": + requestFlags |= unix.MS_NOEXEC + expectedFlags |= unix.ST_NOEXEC + case "exec": + requestFlags &= ^uintptr(unix.MS_NOEXEC) + expectedFlags &= ^uintptr(unix.ST_NOEXEC) + case "nosuid": + requestFlags |= unix.MS_NOSUID + expectedFlags |= unix.ST_NOSUID + case "suid": + requestFlags &= ^uintptr(unix.MS_NOSUID) + expectedFlags &= ^uintptr(unix.ST_NOSUID) + case "ro": + requestFlags |= unix.MS_RDONLY + expectedFlags |= unix.ST_RDONLY + case "rw": + requestFlags &= ^uintptr(unix.MS_RDONLY) + expectedFlags &= ^uintptr(unix.ST_RDONLY) + } + } + switch m.Type { + case "bind": + // Do the bind mount. + logrus.Debugf("bind mounting %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination)) + if err := unix.Mount(m.Source, target, "", requestFlags, ""); err != nil { + return undoBinds, fmt.Errorf("error bind mounting %q from host to %q in mount namespace (%q): %w", m.Source, m.Destination, target, err) + } + logrus.Debugf("bind mounted %q to %q", m.Source, target) + case "tmpfs": + // Mount a tmpfs. + if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil { + return undoBinds, fmt.Errorf("error mounting tmpfs to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err) + } + logrus.Debugf("mounted a tmpfs to %q", target) + case "overlay": + // Mount a overlay. + if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil { + return undoBinds, fmt.Errorf("error mounting overlay to %q in mount namespace (%q, %q): %w", m.Destination, target, strings.Join(m.Options, ","), err) + } + logrus.Debugf("mounted a overlay to %q", target) + } + if err = unix.Statfs(target, &fs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", target, err) + } + if uintptr(fs.Flags)&expectedFlags != expectedFlags { + if err := unix.Mount(target, target, "bind", requestFlags|unix.MS_REMOUNT, ""); err != nil { + return undoBinds, fmt.Errorf("error remounting %q in mount namespace with expected flags: %w", target, err) + } + } + } + + // Set up any read-only paths that we need to. If we're running inside + // of a container, some of these locations will already be read-only. + for _, roPath := range spec.Linux.ReadonlyPaths { + r := filepath.Join(spec.Root.Path, roPath) + target, err := filepath.EvalSymlinks(r) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // No target, no problem. + continue + } + return undoBinds, fmt.Errorf("error checking %q for symlinks before marking it read-only: %w", r, err) + } + // Check if the location is already read-only. + var fs unix.Statfs_t + if err = unix.Statfs(target, &fs); err != nil { + if errors.Is(err, os.ErrNotExist) { + // No target, no problem. + continue + } + return undoBinds, fmt.Errorf("error checking if directory %q is already read-only: %w", target, err) + } + if fs.Flags&unix.ST_RDONLY != 0 { + continue + } + // Mount the location over itself, so that we can remount it as read-only. + roFlags := uintptr(unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY) + if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REC, ""); err != nil { + if errors.Is(err, os.ErrNotExist) { + // No target, no problem. + continue + } + return undoBinds, fmt.Errorf("error bind mounting %q onto itself in preparation for making it read-only: %w", target, err) + } + // Remount the location read-only. + if err = unix.Statfs(target, &fs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q was bound read-only: %w", target, err) + } + if fs.Flags&unix.ST_RDONLY == 0 { + if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REMOUNT, ""); err != nil { + return undoBinds, fmt.Errorf("error remounting %q in mount namespace read-only: %w", target, err) + } + } + // Check again. + if err = unix.Statfs(target, &fs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q was remounted read-only: %w", target, err) + } + if fs.Flags&unix.ST_RDONLY == 0 { + return undoBinds, fmt.Errorf("error verifying that %q in mount namespace was remounted read-only: %w", target, err) + } + } + + // Create an empty directory for to use for masking directories. + roEmptyDir := filepath.Join(bundlePath, "empty") + if len(spec.Linux.MaskedPaths) > 0 { + if err := os.Mkdir(roEmptyDir, 0700); err != nil { + return undoBinds, fmt.Errorf("error creating empty directory %q: %w", roEmptyDir, err) + } + } + + // Set up any masked paths that we need to. If we're running inside of + // a container, some of these locations will already be read-only tmpfs + // filesystems or bind mounted to os.DevNull. If we're not running + // inside of a container, and nobody else has done that, we'll do it. + for _, masked := range spec.Linux.MaskedPaths { + t := filepath.Join(spec.Root.Path, masked) + target, err := filepath.EvalSymlinks(t) + if err != nil { + target = t + } + // Get some info about the target. + targetinfo, err := os.Stat(target) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // No target, no problem. + continue + } + return undoBinds, fmt.Errorf("error examining %q for masking in mount namespace: %w", target, err) + } + if targetinfo.IsDir() { + // The target's a directory. Check if it's a read-only filesystem. + var statfs unix.Statfs_t + if err = unix.Statfs(target, &statfs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q is a mountpoint: %w", target, err) + } + isReadOnly := statfs.Flags&unix.MS_RDONLY != 0 + // Check if any of the IDs we're mapping could read it. + var stat unix.Stat_t + if err = unix.Stat(target, &stat); err != nil { + return undoBinds, fmt.Errorf("error checking permissions on directory %q: %w", target, err) + } + isAccessible := false + if stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 { + isAccessible = true + } + if !isAccessible && stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 { + if len(spec.Linux.GIDMappings) > 0 { + for _, mapping := range spec.Linux.GIDMappings { + if stat.Gid >= mapping.ContainerID && stat.Gid < mapping.ContainerID+mapping.Size { + isAccessible = true + break + } + } + } + } + if !isAccessible && stat.Mode&unix.S_IRUSR|unix.S_IXUSR != 0 { + if len(spec.Linux.UIDMappings) > 0 { + for _, mapping := range spec.Linux.UIDMappings { + if stat.Uid >= mapping.ContainerID && stat.Uid < mapping.ContainerID+mapping.Size { + isAccessible = true + break + } + } + } + } + // Check if it's empty. + hasContent := false + directory, err := os.Open(target) + if err != nil { + if !os.IsPermission(err) { + return undoBinds, fmt.Errorf("error opening directory %q: %w", target, err) + } + } else { + names, err := directory.Readdirnames(0) + directory.Close() + if err != nil { + return undoBinds, fmt.Errorf("error reading contents of directory %q: %w", target, err) + } + hasContent = false + for _, name := range names { + switch name { + case ".", "..": + continue + default: + hasContent = true + } + if hasContent { + break + } + } + } + // The target's a directory, so read-only bind mount an empty directory on it. + roFlags := uintptr(syscall.MS_BIND | syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RDONLY) + if !isReadOnly || (hasContent && isAccessible) { + if err = unix.Mount(roEmptyDir, target, "bind", roFlags, ""); err != nil { + return undoBinds, fmt.Errorf("error masking directory %q in mount namespace: %w", target, err) + } + if err = unix.Statfs(target, &fs); err != nil { + return undoBinds, fmt.Errorf("error checking if directory %q was mounted read-only in mount namespace: %w", target, err) + } + if fs.Flags&unix.ST_RDONLY == 0 { + if err = unix.Mount(target, target, "", roFlags|syscall.MS_REMOUNT, ""); err != nil { + return undoBinds, fmt.Errorf("error making sure directory %q in mount namespace is read only: %w", target, err) + } + } + } + } else { + // If the target's is not a directory or os.DevNull, bind mount os.DevNull over it. + if !isDevNull(targetinfo) { + if err = unix.Mount(os.DevNull, target, "", uintptr(syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_PRIVATE), ""); err != nil { + return undoBinds, fmt.Errorf("error masking non-directory %q in mount namespace: %w", target, err) + } + } + } + } + return undoBinds, nil +} + +// setPdeathsig sets a parent-death signal for the process +func setPdeathsig(cmd *exec.Cmd) { + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL +} diff --git a/vendor/github.com/containers/buildah/copier/copier.go b/vendor/github.com/containers/buildah/copier/copier.go index 3c7b021e1..de464ab52 100644 --- a/vendor/github.com/containers/buildah/copier/copier.go +++ b/vendor/github.com/containers/buildah/copier/copier.go @@ -1558,7 +1558,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } else { // FreeBSD can return EISDIR for "mkdir /": // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739. - if !os.IsExist(err) && !errors.Is(err, syscall.EISDIR) { + if !errors.Is(err, os.ErrExist) && !errors.Is(err, syscall.EISDIR) { return fmt.Errorf("copier: put: error checking directory %q: %w", path, err) } } @@ -1581,7 +1581,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } createFile := func(path string, tr *tar.Reader) (int64, error) { f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC|os.O_EXCL, 0600) - if err != nil && os.IsExist(err) { + if err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err2 := os.Lstat(path); err2 == nil && st.IsDir() { return 0, fmt.Errorf("copier: put: error creating file at %q: %w", path, err) @@ -1626,7 +1626,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM return errorResponse("copier: put: %s (%s): exists but is not a directory", req.Directory, targetDirectory) } } else { - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return errorResponse("copier: put: %s: %v", req.Directory, err) } if err := ensureDirectoryUnderRoot(req.Directory); err != nil { @@ -1738,7 +1738,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM if linkTarget, err = resolvePath(targetDirectory, filepath.Join(req.Root, filepath.FromSlash(hdr.Linkname)), true, nil); err != nil { return fmt.Errorf("error resolving hardlink target path %q under root %q", hdr.Linkname, req.Root) } - if err = os.Link(linkTarget, path); err != nil && os.IsExist(err) { + if err = os.Link(linkTarget, path); err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { break @@ -1753,7 +1753,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM // todo: the general solution requires resolving to an absolute path, handling // renaming, and then possibly converting back to a relative symlink // } - if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && os.IsExist(err) { + if err = os.Symlink(filepath.FromSlash(hdr.Linkname), filepath.FromSlash(path)); err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { break @@ -1768,7 +1768,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM ignoredItems[nameBeforeRenaming] = struct{}{} goto nextHeader } - if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { + if err = mknod(path, chrMode(0600), int(mkdev(devMajor, devMinor))); err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { break @@ -1783,7 +1783,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM ignoredItems[nameBeforeRenaming] = struct{}{} goto nextHeader } - if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && os.IsExist(err) { + if err = mknod(path, blkMode(0600), int(mkdev(devMajor, devMinor))); err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { break @@ -1794,7 +1794,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM } } case tar.TypeDir: - if err = os.Mkdir(path, 0700); err != nil && os.IsExist(err) { + if err = os.Mkdir(path, 0700); err != nil && errors.Is(err, os.ErrExist) { if st, stErr := os.Lstat(path); stErr == nil && !st.IsDir() { if req.PutOptions.NoOverwriteNonDirDir { break @@ -1821,7 +1821,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM // the archive more than once for whatever reason directoryModes[path] = mode case tar.TypeFifo: - if err = mkfifo(path, 0600); err != nil && os.IsExist(err) { + if err = mkfifo(path, 0600); err != nil && errors.Is(err, os.ErrExist) { if req.PutOptions.NoOverwriteDirNonDir { if st, err := os.Lstat(path); err == nil && st.IsDir() { break @@ -1943,7 +1943,7 @@ func copierHandlerMkdir(req request, idMappings *idtools.IDMappings) (*response, } else { // FreeBSD can return EISDIR for "mkdir /": // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=59739. - if !os.IsExist(err) && !errors.Is(err, syscall.EISDIR) { + if !errors.Is(err, os.ErrExist) && !errors.Is(err, syscall.EISDIR) { return errorResponse("copier: mkdir: error checking directory %q: %v", path, err) } } diff --git a/vendor/github.com/containers/buildah/define/build.go b/vendor/github.com/containers/buildah/define/build.go index 501f85ff2..352280433 100644 --- a/vendor/github.com/containers/buildah/define/build.go +++ b/vendor/github.com/containers/buildah/define/build.go @@ -5,6 +5,7 @@ import ( "time" nettypes "github.com/containers/common/libnetwork/types" + "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" encconfig "github.com/containers/ocicrypt/config" "github.com/containers/storage/pkg/archive" @@ -136,6 +137,16 @@ type BuildOptions struct { RuntimeArgs []string // TransientMounts is a list of mounts that won't be kept in the image. TransientMounts []string + // CacheFrom specifies any remote repository which can be treated as + // potential cache source. + CacheFrom reference.Named + // CacheTo specifies any remote repository which can be treated as + // potential cache destination. + CacheTo reference.Named + // CacheTTL specifies duration, if specified using `--cache-ttl` then + // cache intermediate images under this duration will be considered as + // valid cache sources and images outside this duration will be ignored. + CacheTTL time.Duration // Compression specifies the type of compression which is applied to // layer blobs. The default is to not use compression, but // archive.Gzip is recommended. diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go index 07d900811..77e2529f8 100644 --- a/vendor/github.com/containers/buildah/define/types.go +++ b/vendor/github.com/containers/buildah/define/types.go @@ -30,7 +30,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.27.0-dev" + Version = "1.27.0" // DefaultRuntime if containers.conf fails. DefaultRuntime = "runc" @@ -137,12 +137,7 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err } return "", "", fmt.Errorf("cloning %q to %q:\n%s: %w", url, name, string(combinedOutput), err) } - // Check if git url specifies any subdir - // if subdir is there switch to subdir. - if gitSubDir != "" { - name = filepath.Join(name, gitSubDir) - } - return name, "", nil + return name, gitSubDir, nil } if strings.HasPrefix(url, "github.com/") { ghurl := url @@ -178,11 +173,13 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err return "", "", errors.New("unreachable code reached") } -func cloneToDirectory(url, dir string) ([]byte, string, error) { +// parseGitBuildContext parses git build context to `repo`, `sub-dir` +// `branch/commit`, accepts GitBuildContext in the format of +// `repourl.git[#[branch-or-commit]:subdir]`. +func parseGitBuildContext(url string) (string, string, string) { gitSubdir := "" gitBranch := "" gitBranchPart := strings.Split(url, "#") - var cmd *exec.Cmd if len(gitBranchPart) > 1 { // check if string contains path to a subdir gitSubDirPart := strings.Split(gitBranchPart[1], ":") @@ -191,16 +188,52 @@ func cloneToDirectory(url, dir string) ([]byte, string, error) { } gitBranch = gitSubDirPart[0] } - if gitBranch == "" { - logrus.Debugf("cloning %q to %q", gitBranchPart[0], dir) - cmd = exec.Command("git", "clone", "--recurse-submodules", gitBranchPart[0], dir) - } else { - logrus.Debugf("cloning repo %q and branch %q to %q", gitBranchPart[0], gitBranch, dir) - cmd = exec.Command("git", "clone", "--recurse-submodules", "-b", gitBranch, gitBranchPart[0], dir) - } + return gitBranchPart[0], gitSubdir, gitBranch +} +func cloneToDirectory(url, dir string) ([]byte, string, error) { + var cmd *exec.Cmd + gitRepo, gitSubdir, gitBranch := parseGitBuildContext(url) + // init repo + cmd = exec.Command("git", "init", dir) combinedOutput, err := cmd.CombinedOutput() - return combinedOutput, gitSubdir, err + if err != nil { + return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git init`: %w", err) + } + // add origin + cmd = exec.Command("git", "remote", "add", "origin", gitRepo) + cmd.Dir = dir + combinedOutput, err = cmd.CombinedOutput() + if err != nil { + return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git remote add`: %w", err) + } + // fetch required branch or commit and perform checkout + // Always default to `HEAD` if nothing specified + fetch := "HEAD" + if gitBranch != "" { + fetch = gitBranch + } + logrus.Debugf("fetching repo %q and branch (or commit ID) %q to %q", gitRepo, fetch, dir) + cmd = exec.Command("git", "fetch", "--depth=1", "origin", "--", fetch) + cmd.Dir = dir + combinedOutput, err = cmd.CombinedOutput() + if err != nil { + return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git fetch`: %w", err) + } + if fetch == "HEAD" { + // We fetched default branch therefore + // we don't have any valid `branch` or + // `commit` name hence checkout detached + // `FETCH_HEAD` + fetch = "FETCH_HEAD" + } + cmd = exec.Command("git", "checkout", fetch) + cmd.Dir = dir + combinedOutput, err = cmd.CombinedOutput() + if err != nil { + return combinedOutput, gitSubdir, fmt.Errorf("failed while performing `git checkout`: %w", err) + } + return combinedOutput, gitSubdir, nil } func downloadToDirectory(url, dir string) error { diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod index 652f09112..6a6f6dd9d 100644 --- a/vendor/github.com/containers/buildah/go.mod +++ b/vendor/github.com/containers/buildah/go.mod @@ -4,11 +4,11 @@ go 1.17 require ( github.com/containerd/containerd v1.6.6 - github.com/containernetworking/cni v1.1.1 - github.com/containers/common v0.48.1-0.20220715075726-2ac10faca05a - github.com/containers/image/v5 v5.21.2-0.20220714132403-2bb3f3e44c5c + github.com/containernetworking/cni v1.1.2 + github.com/containers/common v0.49.1 + github.com/containers/image/v5 v5.22.0 github.com/containers/ocicrypt v1.1.5 - github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a + github.com/containers/storage v1.42.0 github.com/docker/distribution v2.8.1+incompatible github.com/docker/docker v20.10.17+incompatible github.com/docker/go-units v0.4.0 @@ -17,16 +17,16 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/mattn/go-shellwords v1.0.12 github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.19.0 + github.com/onsi/gomega v1.20.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 github.com/opencontainers/runc v1.1.3 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 - github.com/opencontainers/runtime-tools v0.9.0 + github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 github.com/opencontainers/selinux v1.10.1 github.com/openshift/imagebuilder v1.2.4-0.20220711175835-4151e43600df github.com/seccomp/libseccomp-golang v0.10.0 - github.com/sirupsen/logrus v1.8.1 + github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.0 @@ -34,13 +34,13 @@ require ( go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220624220833-87e55d714810 + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/BurntSushi/toml v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Microsoft/hcsshim v0.9.3 // indirect github.com/VividCortex/ewma v1.2.0 // indirect @@ -63,6 +63,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/google/go-containerregistry v0.10.0 // indirect github.com/google/go-intervals v0.0.2 // indirect github.com/google/uuid v1.3.0 // indirect @@ -72,7 +73,7 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jinzhu/copier v0.3.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.8 // indirect + github.com/klauspost/compress v1.15.9 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/letsencrypt/boulder v0.0.0-20220331220046-b23ab962616e // indirect github.com/manifoldco/promptui v0.9.0 // indirect @@ -100,7 +101,7 @@ require ( github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect github.com/sylabs/sif/v2 v2.7.1 // indirect github.com/tchap/go-patricia v2.3.0+incompatible // indirect - github.com/theupdateframework/go-tuf v0.3.0 // indirect + github.com/theupdateframework/go-tuf v0.3.1 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/vbatts/tar-split v0.11.2 // indirect diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum index cdd6cf3b4..4f39445ee 100644 --- a/vendor/github.com/containers/buildah/go.sum +++ b/vendor/github.com/containers/buildah/go.sum @@ -92,8 +92,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -183,6 +183,7 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= @@ -219,7 +220,6 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/cilium/ebpf v0.9.0/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -338,18 +338,18 @@ github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfsL4b0mc4k= github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= +github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= +github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= github.com/containernetworking/plugins v1.1.1 h1:+AGfFigZ5TiQH00vhR8qPeSatj53eNGz0C1d3wVYlHE= github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8= -github.com/containers/common v0.48.1-0.20220715075726-2ac10faca05a h1:kdcruVl641VTIm8C3O58WRYcBTbnWCsh6AJymk28ScM= -github.com/containers/common v0.48.1-0.20220715075726-2ac10faca05a/go.mod h1:1dA7JPGoSi83kjf5H4NIrGANyLOULyvFqV1bwvYFEek= -github.com/containers/image/v5 v5.21.2-0.20220712113758-29aec5f7bbbf/go.mod h1:0+N0ZM9mgMmoZZc6uNcgnEsbX85Ne7b29cIW5lqWwVU= -github.com/containers/image/v5 v5.21.2-0.20220714132403-2bb3f3e44c5c h1:ms1Vyzs9Eb17J38aFKrL0+ig2pVwQq3OleaO7VmQuV0= -github.com/containers/image/v5 v5.21.2-0.20220714132403-2bb3f3e44c5c/go.mod h1:ykVAVRj4DhQNMHZDVU+KCtXjWBKpqiUe669eF0WBEEc= +github.com/containers/common v0.49.1 h1:6y4/s2WwYxrv+Cox7fotOo316wuZI+iKKPUQweCYv50= +github.com/containers/common v0.49.1/go.mod h1:ueM5hT0itKqCQvVJDs+EtjornAQtrHYxQJzP2gxeGIg= +github.com/containers/image/v5 v5.22.0 h1:KemxPmD4D2YYOFZN2SgoTk7nBFcnwPiPW0MqjYtknSE= +github.com/containers/image/v5 v5.22.0/go.mod h1:D8Ksv2RNB8qLJ7xe1P3rgJJOSQpahA6amv2Ax++/YO4= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a h1:spAGlqziZjCJL25C6F1zsQY05tfCKE9F5YwtEWWe6hU= github.com/containers/libtrust v0.0.0-20200511145503-9c3a6c22cd9a/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= @@ -360,10 +360,8 @@ github.com/containers/ocicrypt v1.1.3/go.mod h1:xpdkbVAuaH3WzbEabUd5yDsl9SwJA5pA github.com/containers/ocicrypt v1.1.5 h1:UO+gBnBXvMvC7HTXLh0bPgLslfW8HlY+oxYcoSHBcZQ= github.com/containers/ocicrypt v1.1.5/go.mod h1:WgjxPWdTJMqYMjf3M6cuIFFA1/MpyyhIM99YInA+Rvc= github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= -github.com/containers/storage v1.41.0/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s= -github.com/containers/storage v1.41.1-0.20220712184034-d26be7b27860/go.mod h1:uu6HCcijN30xRxW1ZuZRngwFGOlH5NpBWYiNBnDQNRw= -github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a h1:+arJAP0v8kEy5fKRPIELjarjpwUHhB7SyRE0uFXlyKY= -github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a/go.mod h1:4DfR+cPpkXKhJnnyydD3z82DXrnTBT63y1k0QWtM2i4= +github.com/containers/storage v1.42.0 h1:zm2AQD4NDeTB3JQ8X+Wo5+VRqNB+b4ocEd7Qj6ylPJA= +github.com/containers/storage v1.42.0/go.mod h1:JiUJwOgOo1dr2DdOUc1MRe2GCAXABYoYmOdPF8yvH78= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -480,7 +478,6 @@ github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/r/VLSOOIySU= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -859,8 +856,8 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.7/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA= -github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -869,9 +866,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -965,6 +961,7 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= @@ -973,7 +970,6 @@ github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwK github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.1/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= @@ -1041,8 +1037,9 @@ github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+t github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= +github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1060,8 +1057,6 @@ github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84 github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runc v1.1.1-0.20220607072441-a7a45d7d2721/go.mod h1:QvA0UNe48mC1JxcXq0sENIR38+/LdJMLNxuAvtFBhxA= -github.com/opencontainers/runc v1.1.1/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w= github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= @@ -1070,15 +1065,17 @@ github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20201121164853-7413a7f753e1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= -github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7 h1:Rf+QsQGxrYCia8mVyOPnoQZ+vJkZGL+ESWBDUM5s9cQ= +github.com/opencontainers/runtime-tools v0.9.1-0.20220714195903-17b3287fafb7/go.mod h1:/tgP02fPXGHkU3/qKK1Y0Db4yqNyGm03vLq/mzHzcS4= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w= github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= @@ -1157,8 +1154,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -1199,8 +1194,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1261,8 +1257,9 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/theupdateframework/go-tuf v0.3.0 h1:od2sc5+BSkKZhmUG2o2rmruy0BGSmhrbDhCnpxh87X8= github.com/theupdateframework/go-tuf v0.3.0/go.mod h1:E5XP0wXitrFUHe4b8cUcAAdxBW4LbfnqF4WXXGLgWNo= +github.com/theupdateframework/go-tuf v0.3.1 h1:NkjMlCuLcDpHNtsWXY4lTmbbQQ5nOM7JSBbOKEEiI1c= +github.com/theupdateframework/go-tuf v0.3.1/go.mod h1:lhHZ3Vt2pdAh15h0Cc6gWdlI+Okn2ZznD3q/cNjd5jw= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1276,11 +1273,11 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -1699,12 +1696,14 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/vendor/github.com/containers/buildah/image.go b/vendor/github.com/containers/buildah/image.go index ec427da1c..510602469 100644 --- a/vendor/github.com/containers/buildah/image.go +++ b/vendor/github.com/containers/buildah/image.go @@ -747,7 +747,7 @@ func (i *containerImageSource) GetBlob(ctx context.Context, blob types.BlobInfo, } layerFile.Close() } - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { logrus.Debugf("error checking for layer %q in %q: %v", blob.Digest.String(), blobDir, err) } } diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index e098db473..a1810d6ad 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -185,7 +185,8 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B options.Manifest = "" type instance struct { v1.Platform - ID string + ID string + Ref reference.Canonical } var instances []instance var instancesLock sync.Mutex @@ -266,10 +267,10 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B if err != nil { return err } - id, ref = thisID, thisRef instancesLock.Lock() instances = append(instances, instance{ ID: thisID, + Ref: thisRef, Platform: platformSpec, }) instancesLock.Unlock() @@ -284,6 +285,25 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options define.B return "", nil, merr.ErrorOrNil() } + // Reasons for this id, ref assigment w.r.t to use-case: + // + // * Single-platform build: On single platform build we only + // have one built instance i.e on indice 0 of built instances, + // so assign that. + // + // * Multi-platform build with manifestList: If this is a build for + // multiple platforms ( more than one platform ) and --manifest + // option then this assignment is insignificant since it will be + // overriden anyways with the id and ref of manifest list later in + // in this code. + // + // * Multi-platform build without manifest list: If this is a build for + // multiple platforms without --manifest then we are free to return + // id and ref of any one of the image in the instance list so always + // return indice 0 for predictable output instead returning the id and + // ref of the go routine which completed at last. + id, ref = instances[0].ID, instances[0].Ref + if manifestList != "" { rt, err := libimage.RuntimeFromStore(store, nil) if err != nil { @@ -396,6 +416,7 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr for i, d := range dockerfilecontents[1:] { additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) if err != nil { + dockerfiles := dockerfiles[1:] return "", nil, fmt.Errorf("error parsing additional Dockerfile %s: %w", dockerfiles[i], err) } mainNode.Children = append(mainNode.Children, additionalNode.Children...) @@ -662,6 +683,7 @@ func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from stri for i, d := range dockerfilecontents[1:] { additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) if err != nil { + dockerfilenames := dockerfilenames[1:] return nil, fmt.Errorf("error parsing additional Dockerfile %s: %w", dockerfilenames[i], err) } mainNode.Children = append(mainNode.Children, additionalNode.Children...) diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go index c9e2493b3..ddd2dfc48 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -58,6 +58,9 @@ var builtinAllowedBuildArgs = map[string]bool{ // interface. It coordinates the entire build by using one or more // 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 @@ -212,6 +215,9 @@ func newExecutor(logger *logrus.Logger, logPrefix string, store storage.Store, o } exec := Executor{ + cacheFrom: options.CacheFrom, + cacheTo: options.CacheTo, + cacheTTL: options.CacheTTL, containerSuffix: options.ContainerSuffix, logger: logger, stages: make(map[string]*StageExecutor), diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go index d21757f4b..9d8214fbd 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go +++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go @@ -2,6 +2,8 @@ package imagebuildah import ( "context" + "crypto/sha256" + "errors" "fmt" "io" "os" @@ -22,6 +24,7 @@ import ( "github.com/containers/buildah/util" config "github.com/containers/common/pkg/config" cp "github.com/containers/image/v5/copy" + imagedocker "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" is "github.com/containers/image/v5/storage" @@ -35,6 +38,7 @@ import ( v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" "github.com/openshift/imagebuilder" + "github.com/openshift/imagebuilder/dockerfile/command" "github.com/openshift/imagebuilder/dockerfile/parser" "github.com/sirupsen/logrus" ) @@ -123,7 +127,7 @@ func (s *StageExecutor) Preserve(path string) error { } st, err := os.Stat(archivedPath) - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { createdDirPerms := os.FileMode(0755) if err = copier.Mkdir(s.mountPoint, archivedPath, copier.MkdirOptions{ChmodNew: &createdDirPerms}); err != nil { return fmt.Errorf("error ensuring volume path exists: %w", err) @@ -165,7 +169,7 @@ func (s *StageExecutor) Preserve(path string) error { archivedPath := filepath.Join(s.mountPoint, cachedPath) logrus.Debugf("no longer need cache of %q in %q", archivedPath, s.volumeCache[cachedPath]) if err := os.Remove(s.volumeCache[cachedPath]); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { continue } return err @@ -186,7 +190,7 @@ func (s *StageExecutor) volumeCacheInvalidate(path string) error { } for _, cachedPath := range invalidated { if err := os.Remove(s.volumeCache[cachedPath]); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { continue } return err @@ -217,7 +221,7 @@ func (s *StageExecutor) volumeCacheSaveVFS() (mounts []specs.Mount, err error) { logrus.Debugf("contents of volume %q are already cached in %q", archivedPath, cacheFile) continue } - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return nil, err } createdDirPerms := os.FileMode(0755) @@ -867,6 +871,21 @@ func (s *StageExecutor) getImageRootfs(ctx context.Context, image string) (mount return builder.MountPoint, nil } +// getContentSummary generates content summary for cases where we added content and need +// to get summary with updated digests. +func (s *StageExecutor) getContentSummaryAfterAddingContent() string { + contentType, digest := s.builder.ContentDigester.Digest() + summary := contentType + if digest != "" { + if summary != "" { + summary = summary + ":" + } + summary = summary + digest.Encoded() + logrus.Debugf("added content %s", summary) + } + return summary +} + // 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, err error) { var resourceUsage rusage.Rusage @@ -945,6 +964,22 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, s.log(commitMessage) } } + // logCachePulled produces build log for cases when `--cache-from` + // is used and a valid intermediate image is pulled from remote source. + logCachePulled := func(cacheKey string) { + if !s.executor.quiet { + cacheHitMessage := "--> Cache pulled from remote" + fmt.Fprintf(s.executor.out, "%s %s\n", cacheHitMessage, fmt.Sprintf("%s:%s", s.executor.cacheFrom, cacheKey)) + } + } + // logCachePush produces build log for cases when `--cache-to` + // is used and a valid intermediate image is pushed tp remote source. + logCachePush := func(cacheKey string) { + if !s.executor.quiet { + cacheHitMessage := "--> Pushing cache" + fmt.Fprintf(s.executor.out, "%s %s\n", cacheHitMessage, fmt.Sprintf("%s:%s", s.executor.cacheTo, cacheKey)) + } + } logCacheHit := func(cacheID string) { if !s.executor.quiet { cacheHitMessage := "--> Using cache" @@ -960,6 +995,17 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, } } + // Parse and populate buildOutputOption if needed + var buildOutputOption define.BuildOutputOption + canGenerateBuildOutput := (s.executor.buildOutput != "" && lastStage) + if canGenerateBuildOutput { + logrus.Debugf("Generating custom build output with options %q", s.executor.buildOutput) + buildOutputOption, err = parse.GetBuildOutput(s.executor.buildOutput) + if err != nil { + return "", nil, fmt.Errorf("failed to parse build output: %w", err) + } + } + if len(children) == 0 { // There are no steps. if s.builder.FromImageID == "" || s.executor.squash { @@ -970,6 +1016,12 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if imgID, ref, err = s.commit(ctx, s.getCreatedBy(nil, ""), false, s.output, s.executor.squash); err != nil { return "", nil, fmt.Errorf("error committing base container: %w", err) } + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } } else if len(s.executor.labels) > 0 || len(s.executor.annotations) > 0 { // The image would be modified by the labels passed // via the command line, so we need to commit. @@ -977,6 +1029,12 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if imgID, ref, err = s.commit(ctx, s.getCreatedBy(stage.Node, ""), true, s.output, s.executor.squash); err != nil { return "", nil, err } + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } } else { // We don't need to squash the base image, and the // image wouldn't be modified by the command line @@ -985,22 +1043,16 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if imgID, ref, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil { return "", nil, err } - if s.executor.buildOutput != "" && lastStage { - // If we have reached this point then our build is just performing a tag - // and it contains no steps or instructions (i.e Containerfile only contains - // `FROM and nothing else so we will never end up committing this - // but instead just re-tag image. For such use-cases if `-o` or `--output` was - // specified honor that and export the contents of the current build anyways. - logrus.Debugf("Generating custom build output with options %q", s.executor.buildOutput) - buildOutputOption, err := parse.GetBuildOutput(s.executor.buildOutput) - if err != nil { - return "", nil, fmt.Errorf("failed to parse build output: %w", err) - } - if err := s.generateBuildOutput(buildah.CommitOptions{}, buildOutputOption); err != nil { + // If we have reached this point then our build is just performing a tag + // and it contains no steps or instructions (i.e Containerfile only contains + // `FROM and nothing else so we will never end up committing this + // but instead just re-tag image. For such use-cases if `-o` or `--output` was + // specified honor that and export the contents of the current build anyways. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { return "", nil, err } } - } logImageID(imgID) } @@ -1101,15 +1153,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, return "", nil, fmt.Errorf("error building at STEP \"%s\": %w", step.Message, err) } // In case we added content, retrieve its digest. - addedContentType, addedContentDigest := s.builder.ContentDigester.Digest() - addedContentSummary := addedContentType - if addedContentDigest != "" { - if addedContentSummary != "" { - addedContentSummary = addedContentSummary + ":" - } - addedContentSummary = addedContentSummary + addedContentDigest.Encoded() - logrus.Debugf("added content %s", addedContentSummary) - } + addedContentSummary := s.getContentSummaryAfterAddingContent() if moreInstructions { // There are still more instructions to process // for this stage. Make a note of the @@ -1134,6 +1178,12 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, return "", nil, fmt.Errorf("error committing container for step %+v: %w", *step, err) } logImageID(imgID) + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } } else { imgID = "" } @@ -1143,19 +1193,33 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // We're in a multi-layered build. var ( - commitName string - cacheID string - err error - rebase bool - addedContentSummary string + commitName string + cacheID string + cacheKey string + pulledAndUsedCacheImage bool + err error + rebase bool + addedContentSummary string + canMatchCacheOnlyAfterRun bool ) + needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil) + // If we have to commit for this instruction, only assign the // stage's configured output name to the last layer. if lastInstruction { commitName = s.output } + // If --cache-from or --cache-to is specified make sure to populate + // cacheKey since it will be used either while pulling or pushing the + // cache images. + if needsCacheKey { + cacheKey, err = s.generateCacheKey(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) + if err != nil { + return "", nil, fmt.Errorf("failed while generating cache key: %w", err) + } + } // Check if there's already an image based on our parent that // has the same change that we're about to make, so far as we // can tell. @@ -1164,17 +1228,57 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, // determining if a cached layer with the same build args already exists // and that is done in the if block below. if checkForLayers && step.Command != "arg" && !(s.executor.squash && lastInstruction && lastStage) { + // For `COPY` and `ADD`, history entries include digests computed from + // the content that's copied in. We need to compute that information so that + // it can be used to evaluate the cache, which means we need to go ahead + // and copy the content. + canMatchCacheOnlyAfterRun = (step.Command == command.Add || step.Command == command.Copy) + if canMatchCacheOnlyAfterRun { + if err = ib.Run(step, s, noRunsRemaining); err != nil { + logrus.Debugf("Error building at step %+v: %v", *step, err) + return "", nil, fmt.Errorf("error building at STEP \"%s\": %w", step.Message, err) + } + // Retrieve the digest info for the content that we just copied + // into the rootfs. + addedContentSummary = s.getContentSummaryAfterAddingContent() + // regenerate cache key with updated content summary + if needsCacheKey { + cacheKey, err = s.generateCacheKey(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) + if err != nil { + return "", nil, fmt.Errorf("failed while generating cache key: %w", err) + } + } + } cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) if err != nil { return "", nil, fmt.Errorf("error checking if cached image exists from a previous build: %w", err) } + // All the best effort to find cache on localstorage have failed try pulling + // cache from remote repo if `--cache-from` was configured. + if cacheID == "" && s.executor.cacheFrom != nil { + // only attempt to use cache again if pulling was successful + // otherwise do nothing and attempt to run the step, err != nil + // is ignored and will be automatically logged for --log-level debug + if id, err := s.pullCache(ctx, cacheKey); id != "" && err == nil { + logCachePulled(cacheKey) + cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) + if err != nil { + return "", nil, fmt.Errorf("error checking if cached image exists from a previous build: %w", err) + } + if cacheID != "" { + pulledAndUsedCacheImage = true + } + } + } } // If we didn't find a cache entry, or we need to add content // to find the digest of the content to check for a cached // image, run the step so that we can check if the result // matches a cache. - if cacheID == "" { + // We already called ib.Run() for the `canMatchCacheOnlyAfterRun` + // cases above, so we shouldn't do it again. + if cacheID == "" && !canMatchCacheOnlyAfterRun { // Process the instruction directly. if err = ib.Run(step, s, noRunsRemaining); err != nil { logrus.Debugf("Error building at step %+v: %v", *step, err) @@ -1182,14 +1286,13 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, } // In case we added content, retrieve its digest. - addedContentType, addedContentDigest := s.builder.ContentDigester.Digest() - addedContentSummary = addedContentType - if addedContentDigest != "" { - if addedContentSummary != "" { - addedContentSummary = addedContentSummary + ":" + addedContentSummary = s.getContentSummaryAfterAddingContent() + // regenerate cache key with updated content summary + if needsCacheKey { + cacheKey, err = s.generateCacheKey(ctx, node, addedContentSummary, s.stepRequiresLayer(step)) + if err != nil { + return "", nil, fmt.Errorf("failed while generating cache key: %w", err) } - addedContentSummary = addedContentSummary + addedContentDigest.Encoded() - logrus.Debugf("added content %s", addedContentSummary) } // Check if there's already an image based on our parent that @@ -1201,6 +1304,10 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, } } } else { + // This log line is majorly here so we can verify in tests + // that our cache is performing in the most optimal way for + // various cases. + logrus.Debugf("Found a cache hit in the first iteration with id %s", cacheID) // If the instruction would affect our configuration, // process the configuration change so that, if we fall // off the cache path, the filesystem changes from the @@ -1246,15 +1353,61 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string, if err != nil { return "", nil, fmt.Errorf("error committing container for step %+v: %w", *step, err) } + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } } - // Create a squashed version of this image - // if we're supposed to create one and this - // is the last instruction of the last stage. - if s.executor.squash && lastInstruction && lastStage { - imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, true) - if err != nil { - return "", nil, fmt.Errorf("error committing final squash step %+v: %w", *step, err) + // Following step is just built and was not used from + // cache so check if --cache-to was specified if yes + // then attempt pushing this cache to remote repo and + // fail accordingly. + // + // Or + // + // Try to push this cache to remote repository only + // if cache was present on local storage and not + // pulled from remote source while processing this + if s.executor.cacheTo != nil && (!pulledAndUsedCacheImage || cacheID == "") { + logCachePush(cacheKey) + if err = s.pushCache(ctx, imgID, cacheKey); err != nil { + return "", nil, err + } + } + + if lastInstruction && lastStage { + if s.executor.squash { + // Create a squashed version of this image + // if we're supposed to create one and this + // is the last instruction of the last stage. + imgID, ref, err = s.commit(ctx, s.getCreatedBy(node, addedContentSummary), !s.stepRequiresLayer(step), commitName, true) + if err != nil { + return "", nil, fmt.Errorf("error committing final squash step %+v: %w", *step, err) + } + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } + } else if cacheID != "" { + // If we found a valid cache hit and this is lastStage + // and not a squashed build then there is no opportunity + // for us to perform a `commit` later in the code since + // everything will be used from cache. + // + // If above statement is true and --output was provided + // then generate output manually since there is no opportunity + // for us to perform `commit` anywhere in the code. + // Generate build output if needed. + if canGenerateBuildOutput { + if err := s.generateBuildOutput(buildOutputOption); err != nil { + return "", nil, err + } + } } } @@ -1521,6 +1674,114 @@ func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output st return img.ID, ref, nil } +// generateCacheKey returns a computed digest for the current STEP +// running its history and diff against a hash algorithm and this +// generated CacheKey is further used by buildah to lock and decide +// tag for the intermeidate 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) (string, error) { + hash := sha256.New() + var baseHistory []v1.History + var diffIDs []digest.Digest + var manifestType string + var err error + if s.builder.FromImageID != "" { + manifestType, baseHistory, diffIDs, err = s.executor.getImageTypeAndHistoryAndDiffIDs(ctx, s.builder.FromImageID) + if err != nil { + return "", fmt.Errorf("error getting history of base image %q: %w", s.builder.FromImageID, err) + } + for i := 0; i < len(diffIDs); i++ { + fmt.Fprintln(hash, diffIDs[i].String()) + } + } + createdBy := s.getCreatedBy(currNode, addedContentDigest) + fmt.Fprintf(hash, "%t", buildAddsLayer) + fmt.Fprintln(hash, createdBy) + fmt.Fprintln(hash, manifestType) + for _, element := range baseHistory { + fmt.Fprintln(hash, element.CreatedBy) + fmt.Fprintln(hash, element.Author) + fmt.Fprintln(hash, element.Comment) + fmt.Fprintln(hash, element.Created) + fmt.Fprintf(hash, "%t", element.EmptyLayer) + fmt.Fprintln(hash) + } + return fmt.Sprintf("%x", hash.Sum(nil)), nil +} + +// cacheImageReference is internal function which generates ImageReference from Named repo sources +// and a tag. +func cacheImageReference(repo reference.Named, cachekey string) (types.ImageReference, error) { + tagged, err := reference.WithTag(repo, cachekey) + if err != nil { + return nil, fmt.Errorf("failed generating tagged reference for %q: %w", repo, err) + } + dest, err := imagedocker.NewReference(tagged) + if err != nil { + return nil, fmt.Errorf("failed generating docker reference for %q: %w", tagged, err) + } + return dest, nil +} + +// 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 { + dest, err := cacheImageReference(s.executor.cacheTo, cacheKey) + if err != nil { + return err + } + logrus.Debugf("trying to push cache to dest: %+v from src:%+v", dest, src) + options := buildah.PushOptions{ + Compression: s.executor.compression, + SignaturePolicyPath: s.executor.signaturePolicyPath, + Store: s.executor.store, + SystemContext: s.executor.systemContext, + BlobDirectory: s.executor.blobDirectory, + SignBy: s.executor.signBy, + MaxRetries: s.executor.maxPullPushRetries, + RetryDelay: s.executor.retryPullPushDelay, + } + ref, digest, err := buildah.Push(ctx, src, dest, options) + if err != nil { + return fmt.Errorf("failed pushing cache to %q: %w", dest, err) + } + logrus.Debugf("successfully pushed cache to dest: %+v with ref:%+v and digest: %v", dest, ref, digest) + return nil +} + +// pullCache takes the image source of the cache assuming tag +// already points to the valid cacheKey and pulls the image to +// local storage only if it was not already present on local storage +// 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) (string, error) { + src, err := cacheImageReference(s.executor.cacheFrom, cacheKey) + if err != nil { + return "", err + } + logrus.Debugf("trying to pull cache from remote repo: %+v", src.DockerReference()) + options := buildah.PullOptions{ + SignaturePolicyPath: s.executor.signaturePolicyPath, + Store: s.executor.store, + SystemContext: s.executor.systemContext, + BlobDirectory: s.executor.blobDirectory, + MaxRetries: s.executor.maxPullPushRetries, + RetryDelay: s.executor.retryPullPushDelay, + AllTags: false, + ReportWriter: nil, + PullPolicy: define.PullIfNewer, + } + id, err := buildah.Pull(ctx, src.DockerReference().String(), options) + if err != nil { + logrus.Debugf("failed pulling cache from source %s: %v", src, err) + return "", fmt.Errorf("failed while pulling cache from %q: %w", src, err) + } + logrus.Debugf("successfully pulled cache from repo %s: %s", src, id) + return id, nil +} + // intermediateImageExists returns true 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. func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *parser.Node, addedContentDigest string, buildAddsLayer bool) (string, error) { @@ -1538,6 +1799,17 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p } } for _, image := range images { + // If s.executor.cacheTTL was specified + // then ignore processing image if it + // was created before the specified + // duration. + if int64(s.executor.cacheTTL) != 0 { + timeNow := time.Now() + imageDuration := timeNow.Sub(image.Created) + if s.executor.cacheTTL < imageDuration { + continue + } + } var imageTopLayer *storage.Layer var imageParentLayerID string if image.TopLayer != "" { @@ -1590,15 +1862,6 @@ func (s *StageExecutor) intermediateImageExists(ctx context.Context, currNode *p // or commit via any custom exporter if specified. func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer bool, output string, squash bool) (string, reference.Canonical, error) { ib := s.stage.Builder - var buildOutputOption define.BuildOutputOption - if s.executor.buildOutput != "" { - var err error - logrus.Debugf("Generating custom build output with options %q", s.executor.buildOutput) - buildOutputOption, err = parse.GetBuildOutput(s.executor.buildOutput) - if err != nil { - return "", nil, fmt.Errorf("failed to parse build output: %w", err) - } - } var imageRef types.ImageReference if output != "" { imageRef2, err := s.executor.resolveNameToImageRef(output) @@ -1739,12 +2002,6 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer HistoryTimestamp: s.executor.timestamp, Manifest: s.executor.manifest, } - // generate build output - if s.executor.buildOutput != "" { - if err := s.generateBuildOutput(buildah.CommitOptions{}, buildOutputOption); err != nil { - return "", nil, err - } - } imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) if err != nil { return "", nil, err @@ -1760,7 +2017,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer return imgID, ref, nil } -func (s *StageExecutor) generateBuildOutput(commitOpts buildah.CommitOptions, buildOutputOpts define.BuildOutputOption) error { +func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOption) error { extractRootfsOpts := buildah.ExtractRootfsOptions{} if unshare.IsRootless() { // In order to maintain as much parity as possible @@ -1775,7 +2032,7 @@ func (s *StageExecutor) generateBuildOutput(commitOpts buildah.CommitOptions, bu extractRootfsOpts.StripSetgidBit = true extractRootfsOpts.StripXattrs = true } - rc, errChan, err := s.builder.ExtractRootfs(commitOpts, extractRootfsOpts) + rc, errChan, err := s.builder.ExtractRootfs(buildah.CommitOptions{}, extractRootfsOpts) if err != nil { return fmt.Errorf("failed to extract rootfs from given container image: %w", err) } diff --git a/vendor/github.com/containers/buildah/info.go b/vendor/github.com/containers/buildah/info.go index 04a1fd08f..9155bb318 100644 --- a/vendor/github.com/containers/buildah/info.go +++ b/vendor/github.com/containers/buildah/info.go @@ -3,16 +3,14 @@ package buildah import ( "bufio" "bytes" - "errors" "fmt" - "io/ioutil" "os" "runtime" "strconv" "strings" - "time" "github.com/containerd/containerd/platforms" + putil "github.com/containers/buildah/pkg/util" "github.com/containers/buildah/util" "github.com/containers/storage" "github.com/containers/storage/pkg/system" @@ -83,22 +81,16 @@ func hostInfo() map[string]interface{} { "version": hostDistributionInfo["Version"], } - kv, err := readKernelVersion() + kv, err := putil.ReadKernelVersion() if err != nil { logrus.Error(err, "error reading kernel version") } info["kernel"] = kv - up, err := readUptime() + upDuration, err := putil.ReadUptime() if err != nil { logrus.Error(err, "error reading up time") } - // Convert uptime in seconds to a human-readable format - upSeconds := up + "s" - upDuration, err := time.ParseDuration(upSeconds) - if err != nil { - logrus.Error(err, "error parsing system uptime") - } hoursFound := false var timeBuffer bytes.Buffer @@ -170,30 +162,6 @@ func storeInfo(store storage.Store) (map[string]interface{}, error) { return info, nil } -func readKernelVersion() (string, error) { - buf, err := ioutil.ReadFile("/proc/version") - if err != nil { - return "", err - } - f := bytes.Fields(buf) - if len(f) < 2 { - return string(bytes.TrimSpace(buf)), nil - } - return string(f[2]), nil -} - -func readUptime() (string, error) { - buf, err := ioutil.ReadFile("/proc/uptime") - if err != nil { - return "", err - } - f := bytes.Fields(buf) - if len(f) < 1 { - return "", errors.New("invalid uptime") - } - return string(f[0]), nil -} - // getHostDistributionInfo returns a map containing the host's distribution and version func getHostDistributionInfo() map[string]string { dist := make(map[string]string) diff --git a/vendor/github.com/containers/buildah/pkg/cli/build.go b/vendor/github.com/containers/buildah/pkg/cli/build.go index 98c42453b..f424df11f 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/build.go +++ b/vendor/github.com/containers/buildah/pkg/cli/build.go @@ -19,6 +19,7 @@ import ( "github.com/containers/buildah/pkg/parse" "github.com/containers/buildah/pkg/util" "github.com/containers/common/pkg/auth" + "github.com/containers/image/v5/docker/reference" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -233,10 +234,6 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( return options, nil, nil, errors.New("'rm' and 'force-rm' can only be set with either 'layers' or 'no-cache'") } - if c.Flag("cache-from").Changed { - logrus.Debugf("build --cache-from not enabled, has no effect") - } - if c.Flag("compress").Changed { logrus.Debugf("--compress option specified but is ignored") } @@ -290,6 +287,29 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( iopts.Quiet = true } } + var cacheTo reference.Named + var cacheFrom reference.Named + cacheTo = nil + cacheFrom = nil + if c.Flag("cache-to").Changed { + cacheTo, err = parse.RepoNameToNamedReference(iopts.CacheTo) + if err != nil { + return options, nil, nil, fmt.Errorf("unable to parse value provided `%s` to --cache-to: %w", iopts.CacheTo, err) + } + } + if c.Flag("cache-from").Changed { + cacheFrom, err = parse.RepoNameToNamedReference(iopts.CacheFrom) + if err != nil { + return options, nil, nil, fmt.Errorf("unable to parse value provided `%s` to --cache-from: %w", iopts.CacheTo, err) + } + } + var cacheTTL time.Duration + if c.Flag("cache-ttl").Changed { + cacheTTL, err = time.ParseDuration(iopts.CacheTTL) + if err != nil { + return options, nil, nil, fmt.Errorf("unable to parse value provided %q as --cache-ttl: %w", iopts.CacheTTL, err) + } + } options = define.BuildOptions{ AddCapabilities: iopts.CapAdd, AdditionalBuildContexts: additionalBuildContext, @@ -300,6 +320,9 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) ( Args: args, BlobDirectory: iopts.BlobCache, BuildOutput: iopts.BuildOutput, + CacheFrom: cacheFrom, + CacheTo: cacheTo, + CacheTTL: cacheTTL, CNIConfigDir: iopts.CNIConfigDir, CNIPluginPath: iopts.CNIPlugInPath, CPPFlags: iopts.CPPFlags, diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index 97ab95ee1..fb5691b33 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -54,6 +54,8 @@ type BudResults struct { BuildArg []string BuildContext []string CacheFrom string + CacheTo string + CacheTTL string CertDir string Compress bool Creds string @@ -197,7 +199,9 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet { fs.StringArrayVar(&flags.OCIHooksDir, "hooks-dir", []string{}, "set the OCI hooks directory path (may be set multiple times)") fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder") fs.StringArrayVar(&flags.BuildContext, "build-context", []string{}, "`argument=value` to supply additional build context to the builder") - fs.StringVar(&flags.CacheFrom, "cache-from", "", "images to utilise as potential cache sources. The build process does not currently support caching so this is a NOOP.") + fs.StringVar(&flags.CacheFrom, "cache-from", "", "remote repository to utilise as potential cache source.") + fs.StringVar(&flags.CacheTo, "cache-to", "", "remote repository to utilise as potential cache destination.") + fs.StringVar(&flags.CacheTTL, "cache-ttl", "", "only consider cache images under specified duration.") fs.StringVar(&flags.CertDir, "cert-dir", "", "use certificates at the specified path to access the registry") fs.BoolVar(&flags.Compress, "compress", false, "this is a legacy option, which has no effect on the image") fs.StringArrayVar(&flags.CPPFlags, "cpp-flag", []string{}, "set additional flag to pass to C preprocessor (cpp)") @@ -276,6 +280,8 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions { flagCompletion["build-arg"] = commonComp.AutocompleteNone flagCompletion["build-context"] = commonComp.AutocompleteNone flagCompletion["cache-from"] = commonComp.AutocompleteNone + flagCompletion["cache-to"] = commonComp.AutocompleteNone + flagCompletion["cache-ttl"] = commonComp.AutocompleteNone flagCompletion["cert-dir"] = commonComp.AutocompleteDefault flagCompletion["cpp-flag"] = commonComp.AutocompleteNone flagCompletion["creds"] = commonComp.AutocompleteNone diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go index 6ab10b13c..07bb2195a 100644 --- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go +++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go @@ -250,7 +250,7 @@ func Unmount(contentDir string) error { } // Ignore EINVAL as the specified merge dir is not a mount point - if err := unix.Unmount(mergeDir, 0); err != nil && !os.IsNotExist(err) && err != unix.EINVAL { + if err := unix.Unmount(mergeDir, 0); err != nil && !errors.Is(err, os.ErrNotExist) && err != unix.EINVAL { return fmt.Errorf("unmount overlay %s: %w", mergeDir, err) } return nil @@ -259,7 +259,7 @@ func Unmount(contentDir string) error { func recreate(contentDir string) error { st, err := system.Stat(contentDir) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { return nil } return fmt.Errorf("failed to stat overlay upper directory: %w", err) @@ -293,7 +293,7 @@ func CleanupContent(containerDir string) (Err error) { files, err := ioutil.ReadDir(contentDir) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { return nil } return fmt.Errorf("read directory: %w", err) @@ -305,7 +305,7 @@ func CleanupContent(containerDir string) (Err error) { } } - if err := os.RemoveAll(contentDir); err != nil && !os.IsNotExist(err) { + if err := os.RemoveAll(contentDir); err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("failed to cleanup overlay directory: %w", err) } return nil diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index 26ee4da77..3492ac968 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -19,6 +19,7 @@ import ( internalParse "github.com/containers/buildah/internal/parse" "github.com/containers/buildah/pkg/sshagent" "github.com/containers/common/pkg/parse" + "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/unshare" @@ -48,6 +49,18 @@ const ( BuildahCacheDir = "buildah-cache" ) +// RepoNameToNamedReference parse the raw string to Named reference +func RepoNameToNamedReference(dest string) (reference.Named, error) { + named, err := reference.ParseNormalizedNamed(dest) + if err != nil { + return nil, fmt.Errorf("invalid repo %q: must contain registry and repository: %w", dest, err) + } + if !reference.IsNameOnly(named) { + return nil, fmt.Errorf("repository must contain neither a tag nor digest: %v", named) + } + return named, nil +} + // CommonBuildOptions parses the build options from the bud cli func CommonBuildOptions(c *cobra.Command) (*define.CommonBuildOptions, error) { return CommonBuildOptionsFromFlagSet(c.Flags(), c.Flag) @@ -232,11 +245,11 @@ func parseSecurityOpts(securityOpts []string, commonOpts *define.CommonBuildOpti if _, err := os.Stat(SeccompOverridePath); err == nil { commonOpts.SeccompProfilePath = SeccompOverridePath } else { - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return err } if _, err := os.Stat(SeccompDefaultPath); err != nil { - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return err } } else { @@ -1059,11 +1072,11 @@ func ContainerIgnoreFile(contextDir, path string) ([]string, string, error) { } path = filepath.Join(contextDir, ".containerignore") excludes, err := imagebuilder.ParseIgnore(path) - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { path = filepath.Join(contextDir, ".dockerignore") excludes, err = imagebuilder.ParseIgnore(path) } - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { return excludes, "", nil } return excludes, path, err diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go b/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go new file mode 100644 index 000000000..d185cb45f --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_darwin.go @@ -0,0 +1,10 @@ +package util + +import ( + "errors" + "time" +) + +func ReadUptime() (time.Duration, error) { + return 0, errors.New("readUptime not supported on darwin") +} diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go b/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go new file mode 100644 index 000000000..7112aba38 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_freebsd.go @@ -0,0 +1,25 @@ +package util + +import ( + "time" + "unsafe" + + "golang.org/x/sys/unix" +) + +// For some reason, unix.ClockGettime isn't implemented by x/sys/unix on FreeBSD +func clockGettime(clockid int32, time *unix.Timespec) (err error) { + _, _, e1 := unix.Syscall(unix.SYS_CLOCK_GETTIME, uintptr(clockid), uintptr(unsafe.Pointer(time)), 0) + if e1 != 0 { + return e1 + } + return nil +} + +func ReadUptime() (time.Duration, error) { + var uptime unix.Timespec + if err := clockGettime(unix.CLOCK_UPTIME, &uptime); err != nil { + return 0, err + } + return time.Duration(unix.TimespecToNsec(uptime)), nil +} diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go new file mode 100644 index 000000000..7c8b6ba76 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_linux.go @@ -0,0 +1,28 @@ +package util + +import ( + "bytes" + "errors" + "io/ioutil" + "time" +) + +func ReadUptime() (time.Duration, error) { + buf, err := ioutil.ReadFile("/proc/uptime") + if err != nil { + return 0, err + } + f := bytes.Fields(buf) + if len(f) < 1 { + return 0, errors.New("invalid uptime") + } + + // Convert uptime in seconds to a human-readable format + up := string(f[0]) + upSeconds := up + "s" + upDuration, err := time.ParseDuration(upSeconds) + if err != nil { + return 0, err + } + return upDuration, nil +} diff --git a/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go b/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go new file mode 100644 index 000000000..ef3adac2a --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/uptime_windows.go @@ -0,0 +1,10 @@ +package util + +import ( + "errors" + "time" +) + +func ReadUptime() (time.Duration, error) { + return 0, errors.New("readUptime not supported on windows") +} diff --git a/vendor/github.com/containers/buildah/pkg/util/version_unix.go b/vendor/github.com/containers/buildah/pkg/util/version_unix.go new file mode 100644 index 000000000..88e8b58a2 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/version_unix.go @@ -0,0 +1,19 @@ +//go:build linux || freebsd || darwin +// +build linux freebsd darwin + +package util + +import ( + "bytes" + + "golang.org/x/sys/unix" +) + +func ReadKernelVersion() (string, error) { + var uname unix.Utsname + if err := unix.Uname(&uname); err != nil { + return "", err + } + n := bytes.IndexByte(uname.Release[:], 0) + return string(uname.Release[:n]), nil +} diff --git a/vendor/github.com/containers/buildah/pkg/util/version_windows.go b/vendor/github.com/containers/buildah/pkg/util/version_windows.go new file mode 100644 index 000000000..9acf469f1 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/util/version_windows.go @@ -0,0 +1,10 @@ +package util + +import ( + "errors" +) + +func ReadKernelVersion() (string, error) { + return "", errors.New("readKernelVersion not supported on windows") + +} diff --git a/vendor/github.com/containers/buildah/run_common.go b/vendor/github.com/containers/buildah/run_common.go index b50afec0b..2054c5652 100644 --- a/vendor/github.com/containers/buildah/run_common.go +++ b/vendor/github.com/containers/buildah/run_common.go @@ -331,7 +331,7 @@ func DefaultNamespaceOptions() (define.NamespaceOptions, error) { {Name: string(specs.MountNamespace), Host: false}, {Name: string(specs.NetworkNamespace), Host: cfg.NetNS() == "host"}, {Name: string(specs.PIDNamespace), Host: cfg.PidNS() == "host"}, - {Name: string(specs.UserNamespace), Host: cfg.Containers.UserNS == "host"}, + {Name: string(specs.UserNamespace), Host: cfg.Containers.UserNS == "" || cfg.Containers.UserNS == "host"}, {Name: string(specs.UTSNamespace), Host: cfg.UTSNS() == "host"}, } return options, nil @@ -477,8 +477,10 @@ func runUsingRuntime(options RunOptions, configureNetwork bool, moreCreateArgs [ if stdioPipe, err = runMakeStdioPipe(int(uid), int(gid)); err != nil { return 1, err } - if err = runLabelStdioPipes(stdioPipe, spec.Process.SelinuxLabel, spec.Linux.MountLabel); err != nil { - return 1, err + if spec.Linux != nil { + if err = runLabelStdioPipes(stdioPipe, spec.Process.SelinuxLabel, spec.Linux.MountLabel); err != nil { + return 1, err + } } errorFds = []int{stdioPipe[unix.Stdout][0], stdioPipe[unix.Stderr][0]} closeBeforeReadingErrorFds = []int{stdioPipe[unix.Stdout][1], stdioPipe[unix.Stderr][1]} @@ -1147,7 +1149,7 @@ func (b *Builder) runUsingRuntimeSubproc(isolation define.Isolation, options Run containerStartR.file, containerStartW.file, err = os.Pipe() if err != nil { - return fmt.Errorf("error creating container create pipe: %w", err) + return fmt.Errorf("error creating container start pipe: %w", err) } defer containerStartR.Close() defer containerStartW.Close() @@ -1365,7 +1367,7 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin // the volume contents. If we do need to create it, then we'll // need to populate it, too, so make a note of that. if _, err := os.Stat(volumePath); err != nil { - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { return nil, err } logrus.Debugf("setting up built-in volume path at %q for %q", volumePath, volume) @@ -1391,7 +1393,7 @@ func runSetupBuiltinVolumes(mountLabel, mountPoint, containerDir string, builtin return nil, fmt.Errorf("evaluating path %q: %w", srcPath, err) } stat, err := os.Stat(srcPath) - if err != nil && !os.IsNotExist(err) { + if err != nil && !errors.Is(err, os.ErrNotExist) { return nil, err } // If we need to populate the mounted volume's contents with @@ -1844,7 +1846,7 @@ func (b *Builder) cleanupRunMounts(context *imageTypes.SystemContext, mountpoint var prevErr error for _, path := range artifacts.TmpFiles { err := os.Remove(path) - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { if prevErr != nil { logrus.Error(prevErr) } diff --git a/vendor/github.com/containers/buildah/run_freebsd.go b/vendor/github.com/containers/buildah/run_freebsd.go index c9384d2d2..b8d141eec 100644 --- a/vendor/github.com/containers/buildah/run_freebsd.go +++ b/vendor/github.com/containers/buildah/run_freebsd.go @@ -251,6 +251,7 @@ func (b *Builder) Run(command []string, options RunOptions) error { jconf.Set("enforce_statfs", 0) jconf.Set("devfs_ruleset", 4) jconf.Set("allow.raw_sockets", true) + jconf.Set("allow.chflags", true) jconf.Set("allow.mount", true) jconf.Set("allow.mount.devfs", true) jconf.Set("allow.mount.nullfs", true) diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index 100e223f9..a5d51732f 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -381,7 +381,7 @@ func (b *Builder) setupOCIHooks(config *spec.Spec, hasVolumes bool) (map[string] for _, hDir := range []string{hooks.DefaultDir, hooks.OverrideDir} { manager, err := hooks.New(context.Background(), []string{hDir}, []string{}) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { continue } return nil, err @@ -690,7 +690,7 @@ func setupNamespaces(logger *logrus.Logger, g *generate.Generator, namespaceOpti // by the kernel p := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1)) _, err := os.Stat(p) - if err != nil && !os.IsNotExist(err) { + if err != nil && !errors.Is(err, os.ErrNotExist) { return false, nil, false, err } if err == nil { diff --git a/vendor/github.com/containers/buildah/selinux.go b/vendor/github.com/containers/buildah/selinux.go index b186cb5e9..8cc2bfc62 100644 --- a/vendor/github.com/containers/buildah/selinux.go +++ b/vendor/github.com/containers/buildah/selinux.go @@ -4,6 +4,7 @@ package buildah import ( + "errors" "fmt" "os" @@ -33,7 +34,7 @@ func runLabelStdioPipes(stdioPipe [][]int, processLabel, mountLabel string) erro } for i := range stdioPipe { pipeFdName := fmt.Sprintf("/proc/self/fd/%d", stdioPipe[i][0]) - if err := selinux.SetFileLabel(pipeFdName, pipeContext); err != nil && !os.IsNotExist(err) { + if err := selinux.SetFileLabel(pipeFdName, pipeContext); err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("setting file label on %q: %w", pipeFdName, err) } } diff --git a/vendor/github.com/containers/buildah/util.go b/vendor/github.com/containers/buildah/util.go index 6ebd04a0c..b362dec84 100644 --- a/vendor/github.com/containers/buildah/util.go +++ b/vendor/github.com/containers/buildah/util.go @@ -187,7 +187,7 @@ func IsContainer(id string, store storage.Store) (bool, error) { // Assuming that if the stateFile exists, that this is a Buildah // container. if _, err = os.Stat(filepath.Join(cdir, stateFile)); err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { return false, nil } return false, err diff --git a/vendor/github.com/containers/buildah/util/util.go b/vendor/github.com/containers/buildah/util/util.go index ffebd3146..10b7504a0 100644 --- a/vendor/github.com/containers/buildah/util/util.go +++ b/vendor/github.com/containers/buildah/util/util.go @@ -384,13 +384,15 @@ var ( // fileExistsAndNotADir - Check to see if a file exists // and that it is not a directory. -func fileExistsAndNotADir(path string) bool { +func fileExistsAndNotADir(path string) (bool, error) { file, err := os.Stat(path) - - if file == nil || err != nil || os.IsNotExist(err) { - return false + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + return false, err } - return !file.IsDir() + return !file.IsDir(), nil } // FindLocalRuntime find the local runtime of the @@ -404,7 +406,11 @@ func FindLocalRuntime(runtime string) string { return localRuntime } for _, val := range conf.Engine.OCIRuntimes[runtime] { - if fileExistsAndNotADir(val) { + exists, err := fileExistsAndNotADir(val) + if err != nil { + logrus.Errorf("Failed to determine if file exists and is not a directory: %v", err) + } + if exists { localRuntime = val break } diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 7b44a84fc..e398bac1d 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.49.1-dev" +const Version = "0.49.1" diff --git a/vendor/modules.txt b/vendor/modules.txt index 8d23648f0..f1dcc5ead 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -91,7 +91,7 @@ github.com/containernetworking/cni/pkg/version # github.com/containernetworking/plugins v1.1.1 ## explicit github.com/containernetworking/plugins/pkg/ns -# github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab +# github.com/containers/buildah v1.27.0 ## explicit github.com/containers/buildah github.com/containers/buildah/bind @@ -114,7 +114,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.49.1-0.20220729221035-246800047d46 +# github.com/containers/common v0.49.1 ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/define -- cgit v1.2.3-54-g00ecf