diff options
author | Daniel J Walsh <dwalsh@redhat.com> | 2019-07-23 05:56:00 -0400 |
---|---|---|
committer | Daniel J Walsh <dwalsh@redhat.com> | 2019-07-30 16:48:18 -0400 |
commit | 141c7a5165261b0a75254107b63b2dac22203ebf (patch) | |
tree | aa6b513cf7e28727367ee6d4ba2980fe48bc86f5 /vendor/github.com | |
parent | 680a3838748b297b7c3c462f98b58f82e39218e8 (diff) | |
download | podman-141c7a5165261b0a75254107b63b2dac22203ebf.tar.gz podman-141c7a5165261b0a75254107b63b2dac22203ebf.tar.bz2 podman-141c7a5165261b0a75254107b63b2dac22203ebf.zip |
Vendor in buildah 1.9.2
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Diffstat (limited to 'vendor/github.com')
53 files changed, 3149 insertions, 2010 deletions
diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml new file mode 100644 index 000000000..19cd0c64c --- /dev/null +++ b/vendor/github.com/containers/buildah/.cirrus.yml @@ -0,0 +1,100 @@ +--- + +# Main collection of env. vars to set for all tasks and scripts. +env: + #### + #### Global variables used for all tasks + #### + # Overrides default location (/tmp/cirrus) for repo clone + CIRRUS_WORKING_DIR: "/var/tmp/go/src/github.com/containers/buildah" + # Shell used to execute all script commands + CIRRUS_SHELL: "/bin/bash" + # Automation script path relative to $CIRRUS_WORKING_DIR) + SCRIPT_BASE: "./contrib/cirrus" + # No need to go crazy, but grab enough to cover most PRs + CIRRUS_CLONE_DEPTH: 50 + + #### + #### Cache-image names to test with + #### + # GCE project where images live + IMAGE_PROJECT: "libpod-218412" + FEDORA_CACHE_IMAGE_NAME: "fedora-cloud-base-30-1-2-1559164849" + PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-cloud-base-29-1-2-1559164849" + UBUNTU_CACHE_IMAGE_NAME: "ubuntu-1904-disco-v20190514" # Latest + PRIOR_UBUNTU_CACHE_IMAGE_NAME: "ubuntu-1804-bionic-v20190530" # LTS + + #### + #### Command variables to help avoid duplication + #### + # Command to prefix every output line with a timestamp + # (can't do inline awk script, Cirrus-CI or YAML mangles quoting) + _TIMESTAMP: 'awk --file ${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/timestamp.awk' + _DFCMD: 'df -lhTx tmpfs' + _RAUDITCMD: 'cat /var/log/audit/audit.log' + _UAUDITCMD: 'cat /var/log/kern.log' + _JOURNALCMD: 'journalctl -b' + +gcp_credentials: ENCRYPTED[ae0bf7370f0b6e446bc61d0865a2c55d3e166b3fab9466eb0393e38e1c66a31ca4c71ddc7e0139d47d075c36dd6d3fd7] + +# Default timeout for each task +timeout_in: 120m + +# Default VM to use unless set or modified by task +gce_instance: + image_project: "${IMAGE_PROJECT}" + zone: "us-central1-c" # Required by Cirrus for the time being + cpu: 2 + memory: "4Gb" + disk: 200 # Gigabytes, do not set less than 200 per obscure GCE docs re: I/O performance + image_name: "${FEDORA_CACHE_IMAGE_NAME}" + + +testing_task: + gce_instance: # Only need to specify differences from defaults (above) + matrix: # Duplicate this task for each matrix product. + image_name: "${FEDORA_CACHE_IMAGE_NAME}" + image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" + image_name: "${UBUNTU_CACHE_IMAGE_NAME}" + image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}" + + # Separate scripts for separate outputs, makes debugging easier. + setup_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/setup.sh |& ${_TIMESTAMP}' + build_and_test_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/build_and_test.sh |& ${_TIMESTAMP}' + + # Log collection when job was successful + df_script: '${_DFCMD} || true' + rh_audit_log_script: '${_RAUDITCMD} || true' + ubuntu_audit_log_script: '${_UAUDITCMD} || true' + journal_log_script: '${_JOURNALCMD} || true' + + on_failure: # Script names must be different from above + failure_df_script: '${_DFCMD} || true' + failure_rh_audit_log_script: '${_RAUDITCMD} || true' + failure_ubuntu_audit_log_script: '${_UAUDITCMD} || true' + failure_journal_log_script: '${_JOURNALCMD} || true' + + +# Update metadata on VM images referenced by this repository state +meta_task: + + container: + image: "quay.io/libpod/imgts:latest" # see contrib/imgts + cpu: 1 + memory: 1 + + env: + # Space-separated list of images used by this repository state + IMGNAMES: |- + ${FEDORA_CACHE_IMAGE_NAME} + ${PRIOR_FEDORA_CACHE_IMAGE_NAME} + ${UBUNTU_CACHE_IMAGE_NAME} + ${PRIOR_UBUNTU_CACHE_IMAGE_NAME} + BUILDID: "${CIRRUS_BUILD_ID}" + REPOREF: "${CIRRUS_CHANGE_IN_REPO}" + GCPJSON: ENCRYPTED[d3614d6f5cc0e66be89d4252b3365fd84f14eee0259d4eb47e25fc0bc2842c7937f5ee8c882b7e547b4c5ec4b6733b14] + GCPNAME: ENCRYPTED[8509e6a681b859479ce6aa275bd3c4ac82de5beec6df6057925afc4cd85b7ef2e879066ae8baaa2d453b82958e434578] + GCPPROJECT: ENCRYPTED[cc09b62d0ec6746a3df685e663ad25d9d5af95ef5fd843c96f3d0ec9d7f065dc63216b9c685c9f43a776a1d403991494] + CIRRUS_CLONE_DEPTH: 1 # source not used + + script: '/usr/local/bin/entrypoint.sh |& ${_TIMESTAMP}' diff --git a/vendor/github.com/containers/buildah/.gitignore b/vendor/github.com/containers/buildah/.gitignore index ff8e05bfe..f419aed12 100644 --- a/vendor/github.com/containers/buildah/.gitignore +++ b/vendor/github.com/containers/buildah/.gitignore @@ -2,3 +2,4 @@ docs/buildah*.1 /buildah /imgtype /build/ +tests/tools/build diff --git a/vendor/github.com/containers/buildah/.golangci.yml b/vendor/github.com/containers/buildah/.golangci.yml new file mode 100644 index 000000000..9a34d03e6 --- /dev/null +++ b/vendor/github.com/containers/buildah/.golangci.yml @@ -0,0 +1,44 @@ +--- +run: + build-tags: + - apparmor + - ostree + - seccomp + - selinux + concurrency: 6 + deadline: 5m +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dupl + - errcheck + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + # - gochecknoglobals + # - gochecknoinits + # - goconst + # - gocritic + # - gocyclo + # - golint + # - gosec + # - interfacer + # - lll + # - maligned + # - prealloc + # - scopelint diff --git a/vendor/github.com/containers/buildah/.travis.yml b/vendor/github.com/containers/buildah/.travis.yml index 9199b8f89..8379c649d 100644 --- a/vendor/github.com/containers/buildah/.travis.yml +++ b/vendor/github.com/containers/buildah/.travis.yml @@ -45,11 +45,20 @@ before_install: - sudo apt-get -qq install software-properties-common - sudo add-apt-repository -y ppa:duggan/bats - sudo apt-get update - - sudo apt-get -qq install bats btrfs-tools git libapparmor-dev libc-dev libdevmapper-dev libglib2.0-dev libgpgme11-dev libselinux1-dev linux-libc-dev realpath + - sudo apt-get -qq install bats btrfs-tools git libapparmor-dev libc-dev libdevmapper-dev libglib2.0-dev libgpgme11-dev libselinux1-dev linux-libc-dev realpath e2fslibs-dev libfuse-dev - sudo apt-get -qq update - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce - mkdir /home/travis/auth - sudo mkdir -p /var/lib/containers/storage/overlay + - > + OSTREE_VERSION=v2019.2; + git clone https://github.com/ostreedev/ostree && + pushd ostree && + git checkout $OSTREE_VERSION && + ./autogen.sh --prefix=/usr && + sudo make -j4 install && + popd && + sudo rm -rf ostree install: # Let's create a self signed certificate and get it in the right places - hostname @@ -85,8 +94,9 @@ script: - docker ps --all - docker images - docker rmi localhost:5000/my-alpine - # Setting up Docker Registry is complete, let's do Buildah testing! - - make install.tools install.libseccomp.sudo all runc validate SECURITYTAGS="apparmor seccomp" + # Setting up Docker Registry is complete, let's do Buildah testing! + - make install.tools -j4 + - make install.libseccomp.sudo all runc validate lint SECURITYTAGS="apparmor seccomp" - go test -c -tags "apparmor seccomp `./btrfs_tag.sh` `./libdm_tag.sh` `./ostree_tag.sh` `./selinux_tag.sh`" ./cmd/buildah - tmp=`mktemp -d`; mkdir $tmp/root $tmp/runroot; sudo PATH="$PATH" ./buildah.test -test.v --root $tmp/root --runroot $tmp/runroot --storage-driver vfs --signature-policy `pwd`/tests/policy.json --registries-conf `pwd`/tests/registries.conf - cd tests; sudo PATH="$PATH" ./test_runner.sh diff --git a/vendor/github.com/containers/buildah/CONTRIBUTING.md b/vendor/github.com/containers/buildah/CONTRIBUTING.md index 7dc301a55..8118a240b 100644 --- a/vendor/github.com/containers/buildah/CONTRIBUTING.md +++ b/vendor/github.com/containers/buildah/CONTRIBUTING.md @@ -118,11 +118,18 @@ commit automatically with `git commit -s`. ## Communications -For general questions, or discussions, please use the +For general questions or discussions, please use the IRC group on `irc.freenode.net` called `buildah` that has been setup. -For discussions around issues/bugs and features, you can use the github +### For discussions around issues/bugs and features: + +#### Buildah Mailing List + +You can join the Buildah mailing list by sending an email to `buildah-join@lists.buildah.io` with the word `subscribe` in the subject. You can also go to this [page](https://lists.podman.io/admin/lists/buildah.lists.buildah.io/), then scroll down to the bottom of the page and enter your email and optionally name, then click on the "Subscribe" buton. + +#### GitHub +You can also use the github [issues](https://github.com/containers/buildah/issues) and [PRs](https://github.com/containers/buildah/pulls) diff --git a/vendor/github.com/containers/buildah/Makefile b/vendor/github.com/containers/buildah/Makefile index 3e66a63a0..42ab36a3c 100644 --- a/vendor/github.com/containers/buildah/Makefile +++ b/vendor/github.com/containers/buildah/Makefile @@ -1,3 +1,5 @@ +export GO111MODULE=off + SELINUXTAG := $(shell ./selinux_tag.sh) STORAGETAGS := $(shell ./btrfs_tag.sh) $(shell ./btrfs_installed_tag.sh) $(shell ./libdm_tag.sh) $(shell ./ostree_tag.sh) SECURITYTAGS ?= seccomp $(SELINUXTAG) @@ -13,10 +15,10 @@ GO110 := 1.10 GOVERSION := $(findstring $(GO110),$(shell go version)) GIT_COMMIT ?= $(if $(shell git rev-parse --short HEAD),$(shell git rev-parse --short HEAD),$(error "git failed")) BUILD_INFO := $(if $(shell date +%s),$(shell date +%s),$(error "date failed")) -CNI_COMMIT := $(if $(shell sed -e '\,github.com/containernetworking/cni, !d' -e 's,.* ,,g' vendor.conf),$(shell sed -e '\,github.com/containernetworking/cni, !d' -e 's,.* ,,g' vendor.conf),$(error "sed failed")) STATIC_STORAGETAGS = "containers_image_ostree_stub containers_image_openpgp exclude_graphdriver_devicemapper $(STORAGE_TAGS)" -RUNC_COMMIT := 2c632d1a2de0192c3f18a2542ccb6f30a8719b1f +CNI_COMMIT := $(shell sed -n 's;\tgithub.com/containernetworking/cni \([^ \n]*\).*$\;\1;p' go.mod) +RUNC_COMMIT := $(shell sed -n 's;\tgithub.com/opencontainers/runc \([^ \n]*\).*$\;\1;p' go.mod) LIBSECCOMP_COMMIT := release-2.3 EXTRALDFLAGS := @@ -47,7 +49,7 @@ clean: $(MAKE) -C docs clean .PHONY: docs -docs: ## build the docs on the host +docs: install.tools ## build the docs on the host $(MAKE) -C docs # For vendoring to work right, the checkout directory must be such that our top @@ -62,7 +64,7 @@ deps: gopath env GOPATH=$(shell cd ../../../.. ; pwd) vndr .PHONY: validate -validate: +validate: install.tools # Run gofmt on version 1.11 and higher ifneq ($(GO110),$(GOVERSION)) @./tests/validate/gofmt.sh @@ -70,15 +72,10 @@ endif @./tests/validate/whitespace.sh @./tests/validate/govet.sh @./tests/validate/git-validation.sh - @./tests/validate/gometalinter.sh . cmd/buildah .PHONY: install.tools install.tools: - $(GO) get -u $(BUILDFLAGS) github.com/cpuguy83/go-md2man - $(GO) get -u $(BUILDFLAGS) github.com/vbatts/git-validation - $(GO) get -u $(BUILDFLAGS) github.com/onsi/ginkgo/ginkgo - $(GO) get -u $(BUILDFLAGS) gopkg.in/alecthomas/gometalinter.v1 - $(GOPATH)/bin/gometalinter.v1 -i + make -C tests/tools .PHONY: runc runc: gopath @@ -119,8 +116,8 @@ install.runc: install -m 755 ../../opencontainers/runc/runc $(DESTDIR)/$(BINDIR)/ .PHONY: test-integration -test-integration: - ginkgo -v tests/e2e/. +test-integration: install.tools + ./tests/tools/ginkgo $(BUILDFLAGS) -v tests/e2e/. cd tests; ./test_runner.sh tests/testreport/testreport: tests/testreport/testreport.go @@ -133,11 +130,13 @@ test-unit: tests/testreport/testreport mkdir -p $$tmp/root $$tmp/runroot; \ $(GO) test -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" ./cmd/buildah -args -root $$tmp/root -runroot $$tmp/runroot -storage-driver vfs -signature-policy $(shell pwd)/tests/policy.json -registries-conf $(shell pwd)/tests/registries.conf -.PHONY: .install.vndr -.install.vndr: - $(GO) get -u github.com/LK4D4/vndr - .PHONY: vendor -vendor: vendor.conf .install.vndr - $(GOPATH)/bin/vndr \ - -whitelist "github.com/onsi/gomega" +vendor: + export GO111MODULE=on \ + $(GO) mod tidy && \ + $(GO) mod vendor && \ + $(GO) mod verify + +.PHONY: lint +lint: install.tools + ./tests/tools/build/golangci-lint run diff --git a/vendor/github.com/containers/buildah/add.go b/vendor/github.com/containers/buildah/add.go index b03aa65b2..6ff9f5250 100644 --- a/vendor/github.com/containers/buildah/add.go +++ b/vendor/github.com/containers/buildah/add.go @@ -278,7 +278,7 @@ func addHelper(excludes *fileutils.PatternMatcher, extract bool, dest string, de return errors.Wrapf(err, "error creating directory %q", dest) } logrus.Debugf("copying %q to %q", esrc+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*") - if excludes == nil { + if excludes == nil || !excludes.Exclusions() { if err = copyWithTar(esrc, dest); err != nil { return errors.Wrapf(err, "error copying %q to %q", esrc, dest) } @@ -310,7 +310,7 @@ func addHelper(excludes *fileutils.PatternMatcher, extract bool, dest string, de return addHelperDirectory(esrc, path, filepath.Join(dest, fpath), info, hostOwner, times) } if info.Mode()&os.ModeSymlink == os.ModeSymlink { - return addHelperSymlink(path, filepath.Join(dest, fpath), info, hostOwner, times) + return addHelperSymlink(path, filepath.Join(dest, fpath), hostOwner, times) } if !info.Mode().IsRegular() { return errors.Errorf("error copying %q to %q: source is not a regular file; file mode is %s", path, dest, info.Mode()) @@ -368,7 +368,7 @@ func addHelperDirectory(esrc, path, dest string, info os.FileInfo, hostOwner idt return nil } -func addHelperSymlink(src, dest string, info os.FileInfo, hostOwner idtools.IDPair, times []syscall.Timespec) error { +func addHelperSymlink(src, dest string, hostOwner idtools.IDPair, times []syscall.Timespec) error { linkContents, err := os.Readlink(src) if err != nil { return errors.Wrapf(err, "error reading contents of symbolic link at %q", src) diff --git a/vendor/github.com/containers/buildah/bind/util.go b/vendor/github.com/containers/buildah/bind/util.go index 93ba4e2b7..5115368d7 100644 --- a/vendor/github.com/containers/buildah/bind/util.go +++ b/vendor/github.com/containers/buildah/bind/util.go @@ -25,15 +25,3 @@ func stripNoBindOption(spec *specs.Spec) { } } } - -func dedupeStringSlice(slice []string) []string { - done := make([]string, 0, len(slice)) - m := make(map[string]struct{}) - for _, s := range slice { - if _, present := m[s]; !present { - m[s] = struct{}{} - done = append(done, s) - } - } - return done -} diff --git a/vendor/github.com/containers/buildah/buildah.go b/vendor/github.com/containers/buildah/buildah.go index 329835d7a..ba5a1c77e 100644 --- a/vendor/github.com/containers/buildah/buildah.go +++ b/vendor/github.com/containers/buildah/buildah.go @@ -15,7 +15,7 @@ import ( "github.com/containers/image/types" "github.com/containers/storage" "github.com/containers/storage/pkg/ioutils" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -26,7 +26,7 @@ const ( Package = "buildah" // Version for the Package. Bump version in contrib/rpm/buildah.spec // too. - Version = "1.9.0" + Version = "1.9.2" // The value we use to identify what type of information, currently a // serialized Builder structure, we are using as per-container state. // This should only be changed when we make incompatible changes to @@ -119,6 +119,9 @@ type Builder struct { // FromImageID is the ID of the source image which was used to create // the container, if one was used. It should not be modified. FromImageID string `json:"image-id"` + // FromImageDigest is the digest of the source image which was used to + // create the container, if one was used. It should not be modified. + FromImageDigest string `json:"image-digest"` // Config is the source image's configuration. It should not be // modified. Config []byte `json:"config,omitempty"` @@ -200,6 +203,7 @@ type BuilderInfo struct { Type string FromImage string FromImageID string + FromImageDigest string Config string Manifest string Container string @@ -243,6 +247,7 @@ func GetBuildInfo(b *Builder) BuilderInfo { Type: b.Type, FromImage: b.FromImage, FromImageID: b.FromImageID, + FromImageDigest: b.FromImageDigest, Config: string(b.Config), Manifest: string(b.Manifest), Container: b.Container, diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt index d35d79dfb..9f64f903b 100644 --- a/vendor/github.com/containers/buildah/changelog.txt +++ b/vendor/github.com/containers/buildah/changelog.txt @@ -1,3 +1,86 @@ +- Changelog for v1.9.2 (2019-07-19) + * from/import: record the base image's digest, if it has one + * Fix CNI version retrieval to not require network connection + * Add misspell linter and apply fixes + * Add goimports linter and apply fixes + * Add stylecheck linter and apply fixes + * Add unconvert linter and apply fixes + * image: make sure we don't try to use zstd compression + * run.bats: skip the "z" flag when testing --mount + * Update to runc v1.0.0-rc8 + * Update to match updated runtime-tools API + * bump github.com/opencontainers/runtime-tools to v0.9.0 + * Build e2e tests using the proper build tags + * Add unparam linter and apply fixes + * Run: correct a typo in the --cap-add help text + * unshare: add a --mount flag + * fix push check image name is not empty + * Bump to v1.9.2-dev + +- Changelog for v1.9.1 (2019-07-12) + * add: fix slow copy with no excludes + * Add errcheck linter and fix missing error check + * Improve tests/tools/Makefile parallelism and abstraction + * Fix response body not closed resource leak + * Switch to golangci-lint + * Add gomod instructions and mailing list links + * On Masked path, check if /dev/null already mounted before mounting + * Update to containers/storage v1.12.13 + * Refactor code in package imagebuildah + * Add rootless podman with NFS issue in documentation + * Add --mount for buildah run + * import method ValidateVolumeOpts from libpod + * Fix typo + * Makefile: set GO111MODULE=off + * rootless: add the built-in slirp DNS server + * Update docker/libnetwork to get rid of outdated sctp package + * Update buildah-login.md + * migrate to go modules + * install.md: mention go modules + * tests/tools: go module for test binaries + * fix --volume splits comma delimited option + * Add bud test for RUN with a priv'd command + * vendor logrus v1.4.2 + * pkg/cli: panic when flags can't be hidden + * pkg/unshare: check all errors + * pull: check error during report write + * run_linux.go: ignore unchecked errors + * conformance test: catch copy error + * chroot/run_test.go: export funcs to actually be executed + * tests/imgtype: ignore error when shutting down the store + * testreport: check json error + * bind/util.go: remove unused func + * rm chroot/util.go + * imagebuildah: remove unused `dedupeStringSlice` + * StageExecutor: EnsureContainerPath: catch error from SecureJoin() + * imagebuildah/build.go: return <expr> instead of branching + * rmi: avoid redundant branching + * conformance tests: nilness: allocate map + * imagebuildah/build.go: avoid redundant `filepath.Join()` + * imagebuildah/build.go: avoid redundant `os.Stat()` + * imagebuildah: omit comparison to bool + * fix "ineffectual assignment" lint errors + * docker: ignore "repeats json tag" lint error + * pkg/unshare: use `...` instead of iterating a slice + * conformance: bud test: use raw strings for regexes + * conformance suite: remove unused func/var + * buildah test suite: remove unused vars/funcs + * testreport: fix golangci-lint errors + * util: remove redundant `return` statement + * chroot: only log clean-up errors + * images_test: ignore golangci-lint error + * blobcache: log error when draining the pipe + * imagebuildah: check errors in deferred calls + * chroot: fix error handling in deferred funcs + * cmd: check all errors + * chroot/run_test.go: check errors + * chroot/run.go: check errors in deferred calls + * imagebuildah.Executor: remove unused onbuild field + * docker/types.go: remove unused struct fields + * util: use strings.ContainsRune instead of index check + * Cirrus: Initial implementation + * Bump to v1.9.1-dev + - Changelog for v1.9.0 (2019-06-15) * buildah-run: fix-out-of-range panic (2) * Bump back to v1.9.0-dev diff --git a/vendor/github.com/containers/buildah/chroot/run.go b/vendor/github.com/containers/buildah/chroot/run.go index ae60d9bbe..db8a4ff06 100644 --- a/vendor/github.com/containers/buildah/chroot/run.go +++ b/vendor/github.com/containers/buildah/chroot/run.go @@ -507,7 +507,9 @@ func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io return 1, err } defer func() { - undoIntermediates() + if undoErr := undoIntermediates(); undoErr != nil { + logrus.Debugf("error cleaning up intermediate mount NS: %v", err) + } }() // Bind mount in our filesystems. @@ -516,7 +518,9 @@ func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io return 1, err } defer func() { - undoChroots() + 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. @@ -565,7 +569,7 @@ func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io cmd.UnshareFlags = syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS requestedUserNS := false for _, ns := range spec.Linux.Namespaces { - if ns.Type == specs.LinuxNamespaceType(specs.UserNamespace) { + if ns.Type == specs.UserNamespace { requestedUserNS = true } } @@ -979,6 +983,21 @@ func makeReadOnly(mntpoint string, flags uintptr) error { 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) { @@ -1259,11 +1278,6 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( if err != nil { target = t } - // Get some info about the null device. - nullinfo, err := os.Stat(os.DevNull) - if err != nil { - return undoBinds, errors.Wrapf(err, "error examining %q for masking in mount namespace", os.DevNull) - } // Get some info about the target. targetinfo, err := os.Stat(target) if err != nil { @@ -1281,12 +1295,11 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( } isReadOnly := statfs.Flags&unix.MS_RDONLY != 0 // Check if any of the IDs we're mapping could read it. - isAccessible := true var stat unix.Stat_t if err = unix.Stat(target, &stat); err != nil { return undoBinds, errors.Wrapf(err, "error checking permissions on directory %q", target) } - isAccessible = false + isAccessible := false if stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 { isAccessible = true } @@ -1352,8 +1365,8 @@ func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func( } } } else { - // The target's not a directory, so bind mount os.DevNull over it, unless it's already os.DevNull. - if !os.SameFile(nullinfo, targetinfo) { + // 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, errors.Wrapf(err, "error masking non-directory %q in mount namespace", target) } diff --git a/vendor/github.com/containers/buildah/chroot/util.go b/vendor/github.com/containers/buildah/chroot/util.go deleted file mode 100644 index 34cc77260..000000000 --- a/vendor/github.com/containers/buildah/chroot/util.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build linux - -package chroot - -func dedupeStringSlice(slice []string) []string { - done := make([]string, 0, len(slice)) - m := make(map[string]struct{}) - for _, s := range slice { - if _, present := m[s]; !present { - m[s] = struct{}{} - done = append(done, s) - } - } - return done -} diff --git a/vendor/github.com/containers/buildah/commit.go b/vendor/github.com/containers/buildah/commit.go index 7a373ea5e..989afad8e 100644 --- a/vendor/github.com/containers/buildah/commit.go +++ b/vendor/github.com/containers/buildah/commit.go @@ -184,8 +184,8 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } // In case we're using caching, decide how to handle compression for a cache. // If we're using blob caching, set it up for the source. - var maybeCachedSrc = types.ImageReference(src) - var maybeCachedDest = types.ImageReference(dest) + maybeCachedSrc := src + maybeCachedDest := dest if options.BlobDirectory != "" { compress := types.PreserveOriginal if options.Compression != archive.Uncompressed { @@ -210,7 +210,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options systemContext.DirForceCompress = true } var manifestBytes []byte - if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, maybeCachedSrc, nil, maybeCachedDest, systemContext, "")); err != nil { + if manifestBytes, err = cp.Image(ctx, policyContext, maybeCachedDest, maybeCachedSrc, getCopyOptions(b.store, options.ReportWriter, nil, systemContext, "")); err != nil { return imgID, nil, "", errors.Wrapf(err, "error copying layers and metadata for container %q", b.ContainerID) } // If we've got more names to attach, and we know how to do that for @@ -301,7 +301,7 @@ func Push(ctx context.Context, image string, dest types.ImageReference, options if err != nil { return nil, "", err } - var maybeCachedSrc = types.ImageReference(src) + maybeCachedSrc := src if options.BlobDirectory != "" { compress := types.PreserveOriginal if options.Compression != archive.Uncompressed { @@ -321,7 +321,7 @@ func Push(ctx context.Context, image string, dest types.ImageReference, options systemContext.DirForceCompress = true } var manifestBytes []byte - if manifestBytes, err = cp.Image(ctx, policyContext, dest, maybeCachedSrc, getCopyOptions(options.Store, options.ReportWriter, maybeCachedSrc, nil, dest, systemContext, options.ManifestType)); err != nil { + if manifestBytes, err = cp.Image(ctx, policyContext, dest, maybeCachedSrc, getCopyOptions(options.Store, options.ReportWriter, nil, systemContext, options.ManifestType)); err != nil { return nil, "", errors.Wrapf(err, "error copying layers and metadata from %q to %q", transports.ImageName(maybeCachedSrc), transports.ImageName(dest)) } if options.ReportWriter != nil { diff --git a/vendor/github.com/containers/buildah/common.go b/vendor/github.com/containers/buildah/common.go index 6b4e2ee90..c08541ac7 100644 --- a/vendor/github.com/containers/buildah/common.go +++ b/vendor/github.com/containers/buildah/common.go @@ -18,7 +18,7 @@ const ( DOCKER = "docker" ) -func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceReference types.ImageReference, sourceSystemContext *types.SystemContext, destinationReference types.ImageReference, destinationSystemContext *types.SystemContext, manifestType string) *cp.Options { +func getCopyOptions(store storage.Store, reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string) *cp.Options { sourceCtx := getSystemContext(store, nil, "") if sourceSystemContext != nil { *sourceCtx = *sourceSystemContext diff --git a/vendor/github.com/containers/buildah/config.go b/vendor/github.com/containers/buildah/config.go index 234f93259..8665e4143 100644 --- a/vendor/github.com/containers/buildah/config.go +++ b/vendor/github.com/containers/buildah/config.go @@ -565,7 +565,7 @@ func (b *Builder) SetHealthcheck(config *docker.HealthConfig) { } // AddPrependedEmptyLayer adds an item to the history that we'll create when -// commiting the image, after any history we inherit from the base image, but +// committing the image, after any history we inherit from the base image, but // before the history item that we'll use to describe the new layer that we're // adding. func (b *Builder) AddPrependedEmptyLayer(created *time.Time, createdBy, author, comment string) { @@ -589,7 +589,7 @@ func (b *Builder) ClearPrependedEmptyLayers() { } // AddAppendedEmptyLayer adds an item to the history that we'll create when -// commiting the image, after the history item that we'll use to describe the +// committing the image, after the history item that we'll use to describe the // new layer that we're adding. func (b *Builder) AddAppendedEmptyLayer(created *time.Time, createdBy, author, comment string) { if created != nil { diff --git a/vendor/github.com/containers/buildah/docker/types.go b/vendor/github.com/containers/buildah/docker/types.go index c59be0e60..2011619f4 100644 --- a/vendor/github.com/containers/buildah/docker/types.go +++ b/vendor/github.com/containers/buildah/docker/types.go @@ -161,18 +161,11 @@ type V1Image struct { // V2Image stores the image configuration type V2Image struct { V1Image - Parent ID `json:"parent,omitempty"` + Parent ID `json:"parent,omitempty"` // nolint:govet RootFS *V2S2RootFS `json:"rootfs,omitempty"` History []V2S2History `json:"history,omitempty"` OSVersion string `json:"os.version,omitempty"` OSFeatures []string `json:"os.features,omitempty"` - - // rawJSON caches the immutable JSON associated with this image. - rawJSON []byte - - // computedID is the ID computed from the hash of the image config. - // Not to be confused with the legacy V1 ID in V1Image. - computedID ID } // github.com/docker/distribution/manifest/versioned.go diff --git a/vendor/github.com/containers/buildah/go.mod b/vendor/github.com/containers/buildah/go.mod new file mode 100644 index 000000000..7e80b0a83 --- /dev/null +++ b/vendor/github.com/containers/buildah/go.mod @@ -0,0 +1,68 @@ +module github.com/containers/buildah + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.2.0 // indirect + github.com/DataDog/zstd v1.4.0 // indirect + github.com/Microsoft/hcsshim v0.8.3 // indirect + github.com/VividCortex/ewma v1.1.1 // indirect + github.com/blang/semver v3.5.0+incompatible // indirect + github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect + github.com/containernetworking/cni v0.7.0-rc2 + github.com/containers/image v2.0.0+incompatible + github.com/containers/storage v1.12.13 + github.com/cyphar/filepath-securejoin v0.2.1 + github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 + github.com/docker/docker-credential-helpers v0.6.1 // indirect + github.com/docker/go-units v0.3.3 + github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect + github.com/etcd-io/bbolt v1.3.2 + github.com/fsouza/go-dockerclient v1.3.0 + github.com/ghodss/yaml v1.0.0 + github.com/gogo/protobuf v1.2.0 // indirect + github.com/hashicorp/go-multierror v1.0.0 + github.com/imdario/mergo v0.3.6 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 // indirect + github.com/klauspost/compress v1.4.1 // indirect + github.com/klauspost/cpuid v1.2.0 // indirect + github.com/klauspost/pgzip v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.4 // indirect + github.com/mattn/go-shellwords v1.0.3 + github.com/mistifyio/go-zfs v2.1.1+incompatible // indirect + github.com/moby/moby v0.0.0-20171005181806-f8806b18b4b9 // indirect + github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c // indirect + github.com/onsi/ginkgo v1.6.0 + github.com/onsi/gomega v1.4.3 + github.com/opencontainers/go-digest v1.0.0-rc1 + github.com/opencontainers/image-spec v1.0.1 + github.com/opencontainers/runc v1.0.0-rc8 + github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 + github.com/opencontainers/runtime-tools v0.9.0 + github.com/opencontainers/selinux v1.2.2 + github.com/openshift/imagebuilder v1.1.0 + github.com/ostreedev/ostree-go v0.0.0-20181112201119-9ab99253d365 // indirect + github.com/pkg/errors v0.8.1 + github.com/pquerna/ffjson v0.0.0-20171002144729-d49c2bc1aa13 // indirect + github.com/seccomp/containers-golang v0.0.0-20180629143253-cdfdaa7543f4 + github.com/seccomp/libseccomp-golang v0.9.0 + github.com/sirupsen/logrus v1.4.2 + github.com/spf13/cobra v0.0.3 + github.com/spf13/pflag v1.0.3 + github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 + github.com/tchap/go-patricia v2.2.6+incompatible // indirect + github.com/ulikunitz/xz v0.5.5 // indirect + github.com/vbatts/tar-split v0.10.2 // indirect + github.com/vbauerster/mpb v3.3.4+incompatible // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.1.0 // indirect + golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc + golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 // indirect + golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect + golang.org/x/sys v0.0.0-20190422165155-953cdadca894 + gopkg.in/yaml.v2 v2.2.2 // indirect + k8s.io/client-go v0.0.0-20181219152756-3dd551c0f083 // indirect +) diff --git a/vendor/github.com/containers/buildah/go.sum b/vendor/github.com/containers/buildah/go.sum new file mode 100644 index 000000000..d8e8f983f --- /dev/null +++ b/vendor/github.com/containers/buildah/go.sum @@ -0,0 +1,176 @@ +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.2.0 h1:OthAm9ZSUx4uAmn3WbPwc06nowWrByRwBsYRhbmFjBs= +github.com/BurntSushi/toml v0.2.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/zstd v1.4.0 h1:vhoV+DUHnRZdKW1i5UMjAk2G4JY8wN4ayRfYDNdEhwo= +github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/hcsshim v0.8.3 h1:KWCdVGOju81E0RL4ndn9/E6I4qMBi6kuPw1W4yBYlCw= +github.com/Microsoft/hcsshim v0.8.3/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/containerd/continuity v0.0.0-20180814194400-c7c5070e6f6e/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containernetworking/cni v0.7.0-rc2 h1:2GGDhbwdWPY53iT7LXy+LBP76Ch2D/hnw1U2zVFfGbk= +github.com/containernetworking/cni v0.7.0-rc2/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0//jzJaYNTVhauk= +github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= +github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= +github.com/containers/storage v1.12.13/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/cyphar/filepath-securejoin v0.2.1 h1:5DPkzz/0MwUpvR4fxASKzgApeq2OMFY5FfYtrX28Coo= +github.com/cyphar/filepath-securejoin v0.2.1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65 h1:4zlOyrJUbYnrvlzChJ+jP2J3i77Jbhm336NEuCv7kZo= +github.com/docker/distribution v0.0.0-20170817175659-5f6282db7d65/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23 h1:mJtkfC9RUrUWHMk0cFDNhVoc9U3k2FRAzEZ+5pqSIHo= +github.com/docker/docker v0.7.3-0.20180827131323-0c5f8d2b9b23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g= +github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= +github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 h1:moehPjPiGUaWdwgOl92xRyFHJyaqXDHcCyW9M6nmCK4= +github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/go-dockerclient v1.3.0 h1:tOXkq/5++XihrAvH5YNwCTdPeQg3XVcC6WI2FVy4ZS0= +github.com/fsouza/go-dockerclient v1.3.0/go.mod h1:IN9UPc4/w7cXiARH2Yg99XxUHbAM+6rAi9hzBVbkWRU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= +github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= +github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= +github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8= +github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/moby/moby v0.0.0-20171005181806-f8806b18b4b9/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4= +github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8 h1:dDCFes8Hj1r/i5qnypONo5jdOme/8HWZC/aNDyhECt0= +github.com/opencontainers/runc v1.0.0-rc8/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 h1:Dliu5QO+4JYWu/yMshaMU7G3JN2POGpwjJN7gjy10Go= +github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +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/selinux v1.2.2 h1:Kx9J6eDG5/24A6DtUquGSpJQ+m2MUTahn4FtGEe8bFg= +github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= +github.com/openshift/imagebuilder v1.1.0 h1:oT704SkwMEzmIMU/+Uv1Wmvt+p10q3v2WuYMeFI18c4= +github.com/openshift/imagebuilder v1.1.0/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= +github.com/ostreedev/ostree-go v0.0.0-20181112201119-9ab99253d365 h1:5DKEDlc/DLftia3h4tk5K0KBiqBXogCc6EarWTlD3fM= +github.com/ostreedev/ostree-go v0.0.0-20181112201119-9ab99253d365/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/ffjson v0.0.0-20171002144729-d49c2bc1aa13 h1:AUK/hm/tPsiNNASdb3J8fySVRZoI7fnK5mlOvdFD43o= +github.com/pquerna/ffjson v0.0.0-20171002144729-d49c2bc1aa13/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= +github.com/seccomp/containers-golang v0.0.0-20180629143253-cdfdaa7543f4 h1:rOG9oHVIndNR14f3HRyBy9UPQYmIPniWqTU1TDdHhq4= +github.com/seccomp/containers-golang v0.0.0-20180629143253-cdfdaa7543f4/go.mod h1:f/98/SnvAzhAEFQJ3u836FePXvcbE8BS0YGMQNn4mhA= +github.com/seccomp/libseccomp-golang v0.9.0 h1:S1pmhdFh5spQtVojA+4GUdWBqvI8ydYHxrx8iR6xN8o= +github.com/seccomp/libseccomp-golang v0.9.0/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/vbatts/tar-split v0.10.2 h1:CXd7HEKGkTLjBMinpObcJZU5Hm8EKlor2a1JtX6msXQ= +github.com/vbatts/tar-split v0.10.2/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= +github.com/vbauerster/mpb v3.3.4+incompatible h1:DDIhnwmgTQIDZo+SWlEr5d6mJBxkOLBwCXPzunhEfJ4= +github.com/vbauerster/mpb v3.3.4+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= +github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGqAlGX3+oCsiL1Q629FL90M= +golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0 h1:1DW40AJQ7AP4nY6ORUGUdkpXyEC9W2GAXcOPaMZK0K8= +golang.org/x/net v0.0.0-20190107210223-45ffb0cd1ba0/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +k8s.io/client-go v0.0.0-20181219152756-3dd551c0f083 h1:+Qf/nITucAbm09aIdxvoA+7X0BwaXmQGVoR8k7Ynk9o= +k8s.io/client-go v0.0.0-20181219152756-3dd551c0f083/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= diff --git a/vendor/github.com/containers/buildah/image.go b/vendor/github.com/containers/buildah/image.go index dc2d323d4..042aa2d00 100644 --- a/vendor/github.com/containers/buildah/image.go +++ b/vendor/github.com/containers/buildah/image.go @@ -23,7 +23,7 @@ import ( "github.com/containers/storage/pkg/ioutils" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -128,6 +128,10 @@ func computeLayerMIMEType(what string, layerCompression archive.Compression) (om // Until the image specs define a media type for xz-compressed layers, even if we know // how to decompress them, we can't try to compress layers with xz. return "", "", errors.New("media type for xz-compressed layers is not defined") + case archive.Zstd: + // Until the image specs define a media type for zstd-compressed layers, even if we know + // how to decompress them, we can't try to compress layers with zstd. + return "", "", errors.New("media type for zstd-compressed layers is not defined") default: logrus.Debugf("compressing %s with unknown compressor(?)", what) } diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index 20d6715f5..85ceceb3a 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -10,29 +10,15 @@ import ( "os" "os/exec" "path/filepath" - "sort" - "strconv" "strings" - "time" "github.com/containers/buildah" - buildahdocker "github.com/containers/buildah/docker" - "github.com/containers/buildah/util" - cp "github.com/containers/image/copy" "github.com/containers/image/docker/reference" - "github.com/containers/image/manifest" - is "github.com/containers/image/storage" - "github.com/containers/image/transports" - "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" - securejoin "github.com/cyphar/filepath-securejoin" - docker "github.com/fsouza/go-dockerclient" - 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/parser" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -45,6 +31,7 @@ const ( Gzip = archive.Gzip Bzip2 = archive.Bzip2 Xz = archive.Xz + Zstd = archive.Zstd Uncompressed = archive.Uncompressed ) @@ -171,1604 +158,6 @@ type BuildOptions struct { Target string } -// Executor is a buildah-based implementation of the imagebuilder.Executor -// interface. It coordinates the entire build by using one StageExecutors to -// handle each stage of the build. -type Executor struct { - stages map[string]*StageExecutor - store storage.Store - contextDir string - pullPolicy buildah.PullPolicy - registry string - ignoreUnrecognizedInstructions bool - quiet bool - runtime string - runtimeArgs []string - transientMounts []Mount - compression archive.Compression - output string - outputFormat string - additionalTags []string - log func(format string, args ...interface{}) - in io.Reader - out io.Writer - err io.Writer - signaturePolicyPath string - systemContext *types.SystemContext - reportWriter io.Writer - isolation buildah.Isolation - namespaceOptions []buildah.NamespaceOption - configureNetwork buildah.NetworkConfigurationPolicy - cniPluginPath string - cniConfigDir string - idmappingOptions *buildah.IDMappingOptions - commonBuildOptions *buildah.CommonBuildOptions - defaultMountsFilePath string - iidfile string - squash bool - labels []string - annotations []string - onbuild []string - layers bool - useCache bool - removeIntermediateCtrs bool - forceRmIntermediateCtrs bool - imageMap map[string]string // Used to map images that we create to handle the AS construct. - containerMap map[string]*buildah.Builder // Used to map from image names to only-created-for-the-rootfs containers. - baseMap map[string]bool // Holds the names of every base image, as given. - rootfsMap map[string]bool // Holds the names of every stage whose rootfs is referenced in a COPY or ADD instruction. - blobDirectory string - excludes []string - unusedArgs map[string]struct{} - buildArgs map[string]string -} - -// StageExecutor bundles up what we need to know when executing one stage of a -// (possibly multi-stage) build. -// Each stage may need to produce an image to be used as the base in a later -// stage (with the last stage's image being the end product of the build), and -// it may need to leave its working container in place so that the container's -// root filesystem's contents can be used as the source for a COPY instruction -// in a later stage. -// Each stage has its own base image, so it starts with its own configuration -// and set of volumes. -// If we're naming the result of the build, only the last stage will apply that -// name to the image that it produces. -type StageExecutor struct { - executor *Executor - index int - stages int - name string - builder *buildah.Builder - preserved int - volumes imagebuilder.VolumeSet - volumeCache map[string]string - volumeCacheInfo map[string]os.FileInfo - mountPoint string - copyFrom string // Used to keep track of the --from flag from COPY and ADD - output string - containerIDs []string -} - -// builtinAllowedBuildArgs is list of built-in allowed build args. Normally we -// complain if we're given values for arguments which have no corresponding ARG -// instruction in the Dockerfile, since that's usually an indication of a user -// error, but for these values we make exceptions and ignore them. -var builtinAllowedBuildArgs = map[string]bool{ - "HTTP_PROXY": true, - "http_proxy": true, - "HTTPS_PROXY": true, - "https_proxy": true, - "FTP_PROXY": true, - "ftp_proxy": true, - "NO_PROXY": true, - "no_proxy": true, -} - -// startStage creates a new stage executor that will be referenced whenever a -// COPY or ADD statement uses a --from=NAME flag. -func (b *Executor) startStage(name string, index, stages int, from, output string) *StageExecutor { - if b.stages == nil { - b.stages = make(map[string]*StageExecutor) - } - stage := &StageExecutor{ - executor: b, - index: index, - stages: stages, - name: name, - volumeCache: make(map[string]string), - volumeCacheInfo: make(map[string]os.FileInfo), - output: output, - } - b.stages[name] = stage - b.stages[from] = stage - if idx := strconv.Itoa(index); idx != name { - b.stages[idx] = stage - } - return stage -} - -// Preserve informs the stage executor that from this point on, it needs to -// ensure that only COPY and ADD instructions can modify the contents of this -// directory or anything below it. -// The StageExecutor handles this by caching the contents of directories which -// have been marked this way before executing a RUN instruction, invalidating -// that cache when an ADD or COPY instruction sets any location under the -// directory as the destination, and using the cache to reset the contents of -// the directory tree after processing each RUN instruction. -// It would be simpler if we could just mark the directory as a read-only bind -// mount of itself during Run(), but the directory is expected to be remain -// writeable while the RUN instruction is being handled, even if any changes -// made within the directory are ultimately discarded. -func (s *StageExecutor) Preserve(path string) error { - logrus.Debugf("PRESERVE %q", path) - if s.volumes.Covers(path) { - // This path is already a subdirectory of a volume path that - // we're already preserving, so there's nothing new to be done - // except ensure that it exists. - archivedPath := filepath.Join(s.mountPoint, path) - if err := os.MkdirAll(archivedPath, 0755); err != nil { - return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) - } - if err := s.volumeCacheInvalidate(path); err != nil { - return errors.Wrapf(err, "error ensuring volume path %q is preserved", archivedPath) - } - return nil - } - // Figure out where the cache for this volume would be stored. - s.preserved++ - cacheDir, err := s.executor.store.ContainerDirectory(s.builder.ContainerID) - if err != nil { - return errors.Errorf("unable to locate temporary directory for container") - } - cacheFile := filepath.Join(cacheDir, fmt.Sprintf("volume%d.tar", s.preserved)) - // Save info about the top level of the location that we'll be archiving. - archivedPath := filepath.Join(s.mountPoint, path) - - // Try and resolve the symlink (if one exists) - // Set archivedPath and path based on whether a symlink is found or not - if symLink, err := resolveSymlink(s.mountPoint, path); err == nil { - archivedPath = filepath.Join(s.mountPoint, symLink) - path = symLink - } else { - return errors.Wrapf(err, "error reading symbolic link to %q", path) - } - - st, err := os.Stat(archivedPath) - if os.IsNotExist(err) { - if err = os.MkdirAll(archivedPath, 0755); err != nil { - return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) - } - st, err = os.Stat(archivedPath) - } - if err != nil { - logrus.Debugf("error reading info about %q: %v", archivedPath, err) - return errors.Wrapf(err, "error reading info about volume path %q", archivedPath) - } - s.volumeCacheInfo[path] = st - if !s.volumes.Add(path) { - // This path is not a subdirectory of a volume path that we're - // already preserving, so adding it to the list should work. - return errors.Errorf("error adding %q to the volume cache", path) - } - s.volumeCache[path] = cacheFile - // Now prune cache files for volumes that are now supplanted by this one. - removed := []string{} - for cachedPath := range s.volumeCache { - // Walk our list of cached volumes, and check that they're - // still in the list of locations that we need to cache. - found := false - for _, volume := range s.volumes { - if volume == cachedPath { - // We need to keep this volume's cache. - found = true - break - } - } - if !found { - // We don't need to keep this volume's cache. Make a - // note to remove it. - removed = append(removed, cachedPath) - } - } - // Actually remove the caches that we decided to remove. - for _, cachedPath := range removed { - 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) { - continue - } - return errors.Wrapf(err, "error removing %q", s.volumeCache[cachedPath]) - } - delete(s.volumeCache, cachedPath) - } - return nil -} - -// Remove any volume cache item which will need to be re-saved because we're -// writing to part of it. -func (s *StageExecutor) volumeCacheInvalidate(path string) error { - invalidated := []string{} - for cachedPath := range s.volumeCache { - if strings.HasPrefix(path, cachedPath+string(os.PathSeparator)) { - invalidated = append(invalidated, cachedPath) - } - } - for _, cachedPath := range invalidated { - if err := os.Remove(s.volumeCache[cachedPath]); err != nil { - if os.IsNotExist(err) { - continue - } - return errors.Wrapf(err, "error removing volume cache %q", s.volumeCache[cachedPath]) - } - archivedPath := filepath.Join(s.mountPoint, cachedPath) - logrus.Debugf("invalidated volume cache for %q from %q", archivedPath, s.volumeCache[cachedPath]) - delete(s.volumeCache, cachedPath) - } - return nil -} - -// Save the contents of each of the executor's list of volumes for which we -// don't already have a cache file. -func (s *StageExecutor) volumeCacheSave() error { - for cachedPath, cacheFile := range s.volumeCache { - archivedPath := filepath.Join(s.mountPoint, cachedPath) - _, err := os.Stat(cacheFile) - if err == nil { - logrus.Debugf("contents of volume %q are already cached in %q", archivedPath, cacheFile) - continue - } - if !os.IsNotExist(err) { - return errors.Wrapf(err, "error checking for cache of %q in %q", archivedPath, cacheFile) - } - if err := os.MkdirAll(archivedPath, 0755); err != nil { - return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) - } - logrus.Debugf("caching contents of volume %q in %q", archivedPath, cacheFile) - cache, err := os.Create(cacheFile) - if err != nil { - return errors.Wrapf(err, "error creating archive at %q", cacheFile) - } - defer cache.Close() - rc, err := archive.Tar(archivedPath, archive.Uncompressed) - if err != nil { - return errors.Wrapf(err, "error archiving %q", archivedPath) - } - defer rc.Close() - _, err = io.Copy(cache, rc) - if err != nil { - return errors.Wrapf(err, "error archiving %q to %q", archivedPath, cacheFile) - } - } - return nil -} - -// Restore the contents of each of the executor's list of volumes. -func (s *StageExecutor) volumeCacheRestore() error { - for cachedPath, cacheFile := range s.volumeCache { - archivedPath := filepath.Join(s.mountPoint, cachedPath) - logrus.Debugf("restoring contents of volume %q from %q", archivedPath, cacheFile) - cache, err := os.Open(cacheFile) - if err != nil { - return errors.Wrapf(err, "error opening archive at %q", cacheFile) - } - defer cache.Close() - if err := os.RemoveAll(archivedPath); err != nil { - return errors.Wrapf(err, "error clearing volume path %q", archivedPath) - } - if err := os.MkdirAll(archivedPath, 0755); err != nil { - return errors.Wrapf(err, "error recreating volume path %q", archivedPath) - } - err = archive.Untar(cache, archivedPath, nil) - if err != nil { - return errors.Wrapf(err, "error extracting archive at %q", archivedPath) - } - if st, ok := s.volumeCacheInfo[cachedPath]; ok { - if err := os.Chmod(archivedPath, st.Mode()); err != nil { - return errors.Wrapf(err, "error restoring permissions on %q", archivedPath) - } - if err := os.Chown(archivedPath, 0, 0); err != nil { - return errors.Wrapf(err, "error setting ownership on %q", archivedPath) - } - if err := os.Chtimes(archivedPath, st.ModTime(), st.ModTime()); err != nil { - return errors.Wrapf(err, "error restoring datestamps on %q", archivedPath) - } - } - } - return nil -} - -// Copy copies data into the working tree. The "Download" field is how -// imagebuilder tells us the instruction was "ADD" and not "COPY" -func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) error { - for _, copy := range copies { - // Check the file and see if part of it is a symlink. - // Convert it to the target if so. To be ultrasafe - // do the same for the mountpoint. - hadFinalPathSeparator := len(copy.Dest) > 0 && copy.Dest[len(copy.Dest)-1] == os.PathSeparator - secureMountPoint, err := securejoin.SecureJoin("", s.mountPoint) - finalPath, err := securejoin.SecureJoin(secureMountPoint, copy.Dest) - if err != nil { - return errors.Wrapf(err, "error resolving symlinks for copy destination %s", copy.Dest) - } - if !strings.HasPrefix(finalPath, secureMountPoint) { - return errors.Wrapf(err, "error resolving copy destination %s", copy.Dest) - } - copy.Dest = strings.TrimPrefix(finalPath, secureMountPoint) - if len(copy.Dest) == 0 || copy.Dest[len(copy.Dest)-1] != os.PathSeparator { - if hadFinalPathSeparator { - copy.Dest += string(os.PathSeparator) - } - } - - if copy.Download { - logrus.Debugf("ADD %#v, %#v", excludes, copy) - } else { - logrus.Debugf("COPY %#v, %#v", excludes, copy) - } - if err := s.volumeCacheInvalidate(copy.Dest); err != nil { - return err - } - sources := []string{} - for _, src := range copy.Src { - contextDir := s.executor.contextDir - copyExcludes := excludes - var idMappingOptions *buildah.IDMappingOptions - if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") { - sources = append(sources, src) - } else if len(copy.From) > 0 { - var srcRoot string - if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index { - srcRoot = other.mountPoint - contextDir = other.mountPoint - idMappingOptions = &other.builder.IDMappingOptions - } else if builder, ok := s.executor.containerMap[copy.From]; ok { - srcRoot = builder.MountPoint - contextDir = builder.MountPoint - idMappingOptions = &builder.IDMappingOptions - } else { - return errors.Errorf("the stage %q has not been built", copy.From) - } - srcSecure, err := securejoin.SecureJoin(srcRoot, src) - if err != nil { - return err - } - // If destination is a folder, we need to take extra care to - // ensure that files are copied with correct names (since - // resolving a symlink may result in a different name). - if hadFinalPathSeparator { - _, srcName := filepath.Split(src) - _, srcNameSecure := filepath.Split(srcSecure) - if srcName != srcNameSecure { - options := buildah.AddAndCopyOptions{ - Chown: copy.Chown, - ContextDir: contextDir, - Excludes: copyExcludes, - } - if err := s.builder.Add(filepath.Join(copy.Dest, srcName), copy.Download, options, srcSecure); err != nil { - return err - } - continue - } - } - sources = append(sources, srcSecure) - - } else { - sources = append(sources, filepath.Join(s.executor.contextDir, src)) - copyExcludes = append(s.executor.excludes, excludes...) - } - options := buildah.AddAndCopyOptions{ - Chown: copy.Chown, - ContextDir: contextDir, - Excludes: copyExcludes, - IDMappingOptions: idMappingOptions, - } - if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil { - return err - } - } - } - return nil -} - -func convertMounts(mounts []Mount) []specs.Mount { - specmounts := []specs.Mount{} - for _, m := range mounts { - s := specs.Mount{ - Destination: m.Destination, - Type: m.Type, - Source: m.Source, - Options: m.Options, - } - specmounts = append(specmounts, s) - } - return specmounts -} - -// Run executes a RUN instruction using the stage's current working container -// as a root directory. -func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { - logrus.Debugf("RUN %#v, %#v", run, config) - if s.builder == nil { - return errors.Errorf("no build container available") - } - stdin := s.executor.in - if stdin == nil { - devNull, err := os.Open(os.DevNull) - if err != nil { - return errors.Errorf("error opening %q for reading: %v", os.DevNull, err) - } - defer devNull.Close() - stdin = devNull - } - options := buildah.RunOptions{ - Hostname: config.Hostname, - Runtime: s.executor.runtime, - Args: s.executor.runtimeArgs, - NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "", - Mounts: convertMounts(s.executor.transientMounts), - Env: config.Env, - User: config.User, - WorkingDir: config.WorkingDir, - Entrypoint: config.Entrypoint, - Cmd: config.Cmd, - Stdin: stdin, - Stdout: s.executor.out, - Stderr: s.executor.err, - Quiet: s.executor.quiet, - NamespaceOptions: s.executor.namespaceOptions, - } - if config.NetworkDisabled { - options.ConfigureNetwork = buildah.NetworkDisabled - } else { - options.ConfigureNetwork = buildah.NetworkEnabled - } - - args := run.Args - if run.Shell { - if len(config.Shell) > 0 && s.builder.Format == buildah.Dockerv2ImageManifest { - args = append(config.Shell, args...) - } else { - args = append([]string{"/bin/sh", "-c"}, args...) - } - } - if err := s.volumeCacheSave(); err != nil { - return err - } - err := s.builder.Run(args, options) - if err2 := s.volumeCacheRestore(); err2 != nil { - if err == nil { - return err2 - } - } - return err -} - -// UnrecognizedInstruction is called when we encounter an instruction that the -// imagebuilder parser didn't understand. -func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error { - errStr := fmt.Sprintf("Build error: Unknown instruction: %q ", strings.ToUpper(step.Command)) - err := fmt.Sprintf(errStr+"%#v", step) - if s.executor.ignoreUnrecognizedInstructions { - logrus.Debugf(err) - return nil - } - - switch logrus.GetLevel() { - case logrus.ErrorLevel: - logrus.Errorf(errStr) - case logrus.DebugLevel: - logrus.Debugf(err) - default: - logrus.Errorf("+(UNHANDLED LOGLEVEL) %#v", step) - } - - return errors.Errorf(err) -} - -// NewExecutor creates a new instance of the imagebuilder.Executor interface. -func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Node) (*Executor, error) { - excludes, err := imagebuilder.ParseDockerignore(options.ContextDirectory) - if err != nil { - return nil, err - } - - exec := Executor{ - store: store, - contextDir: options.ContextDirectory, - excludes: excludes, - pullPolicy: options.PullPolicy, - registry: options.Registry, - ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions, - quiet: options.Quiet, - runtime: options.Runtime, - runtimeArgs: options.RuntimeArgs, - transientMounts: options.TransientMounts, - compression: options.Compression, - output: options.Output, - outputFormat: options.OutputFormat, - additionalTags: options.AdditionalTags, - signaturePolicyPath: options.SignaturePolicyPath, - systemContext: options.SystemContext, - log: options.Log, - in: options.In, - out: options.Out, - err: options.Err, - reportWriter: options.ReportWriter, - isolation: options.Isolation, - namespaceOptions: options.NamespaceOptions, - configureNetwork: options.ConfigureNetwork, - cniPluginPath: options.CNIPluginPath, - cniConfigDir: options.CNIConfigDir, - idmappingOptions: options.IDMappingOptions, - commonBuildOptions: options.CommonBuildOpts, - defaultMountsFilePath: options.DefaultMountsFilePath, - iidfile: options.IIDFile, - squash: options.Squash, - labels: append([]string{}, options.Labels...), - annotations: append([]string{}, options.Annotations...), - layers: options.Layers, - useCache: !options.NoCache, - removeIntermediateCtrs: options.RemoveIntermediateCtrs, - forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs, - imageMap: make(map[string]string), - containerMap: make(map[string]*buildah.Builder), - baseMap: make(map[string]bool), - rootfsMap: make(map[string]bool), - blobDirectory: options.BlobDirectory, - unusedArgs: make(map[string]struct{}), - buildArgs: options.Args, - } - if exec.err == nil { - exec.err = os.Stderr - } - if exec.out == nil { - exec.out = os.Stdout - } - if exec.log == nil { - stepCounter := 0 - exec.log = func(format string, args ...interface{}) { - stepCounter++ - prefix := fmt.Sprintf("STEP %d: ", stepCounter) - suffix := "\n" - fmt.Fprintf(exec.err, prefix+format+suffix, args...) - } - } - for arg := range options.Args { - if _, isBuiltIn := builtinAllowedBuildArgs[arg]; !isBuiltIn { - exec.unusedArgs[arg] = struct{}{} - } - } - for _, line := range mainNode.Children { - node := line - for node != nil { // tokens on this line, though we only care about the first - switch strings.ToUpper(node.Value) { // first token - instruction - case "ARG": - arg := node.Next - if arg != nil { - // We have to be careful here - it's either an argument - // and value, or just an argument, since they can be - // separated by either "=" or whitespace. - list := strings.SplitN(arg.Value, "=", 2) - if _, stillUnused := exec.unusedArgs[list[0]]; stillUnused { - delete(exec.unusedArgs, list[0]) - } - } - } - break - } - } - return &exec, nil -} - -// prepare creates a working container based on the specified image, or if one -// isn't specified, the first argument passed to the first FROM instruction we -// can find in the stage's parsed tree. -func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, from string, initializeIBConfig, rebase bool) (builder *buildah.Builder, err error) { - ib := stage.Builder - node := stage.Node - - if from == "" { - base, err := ib.From(node) - if err != nil { - logrus.Debugf("prepare(node.Children=%#v)", node.Children) - return nil, errors.Wrapf(err, "error determining starting point for build") - } - from = base - } - displayFrom := from - - // stage.Name will be a numeric string for all stages without an "AS" clause - asImageName := stage.Name - if asImageName != "" { - if _, err := strconv.Atoi(asImageName); err != nil { - displayFrom = from + " AS " + asImageName - } else { - asImageName = "" - } - } - - if initializeIBConfig && rebase { - logrus.Debugf("FROM %#v", displayFrom) - if !s.executor.quiet { - s.executor.log("FROM %s", displayFrom) - } - } - - builderOptions := buildah.BuilderOptions{ - Args: ib.Args, - FromImage: from, - PullPolicy: s.executor.pullPolicy, - Registry: s.executor.registry, - BlobDirectory: s.executor.blobDirectory, - SignaturePolicyPath: s.executor.signaturePolicyPath, - ReportWriter: s.executor.reportWriter, - SystemContext: s.executor.systemContext, - Isolation: s.executor.isolation, - NamespaceOptions: s.executor.namespaceOptions, - ConfigureNetwork: s.executor.configureNetwork, - CNIPluginPath: s.executor.cniPluginPath, - CNIConfigDir: s.executor.cniConfigDir, - IDMappingOptions: s.executor.idmappingOptions, - CommonBuildOpts: s.executor.commonBuildOptions, - DefaultMountsFilePath: s.executor.defaultMountsFilePath, - Format: s.executor.outputFormat, - } - - // Check and see if the image is a pseudonym for the end result of a - // previous stage, named by an AS clause in the Dockerfile. - if asImageFound, ok := s.executor.imageMap[from]; ok { - builderOptions.FromImage = asImageFound - } - builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions) - if err != nil { - return nil, errors.Wrapf(err, "error creating build container") - } - - if initializeIBConfig { - volumes := map[string]struct{}{} - for _, v := range builder.Volumes() { - volumes[v] = struct{}{} - } - ports := map[docker.Port]struct{}{} - for _, p := range builder.Ports() { - ports[docker.Port(p)] = struct{}{} - } - dConfig := docker.Config{ - Hostname: builder.Hostname(), - Domainname: builder.Domainname(), - User: builder.User(), - Env: builder.Env(), - Cmd: builder.Cmd(), - Image: from, - Volumes: volumes, - WorkingDir: builder.WorkDir(), - Entrypoint: builder.Entrypoint(), - Labels: builder.Labels(), - Shell: builder.Shell(), - StopSignal: builder.StopSignal(), - OnBuild: builder.OnBuild(), - ExposedPorts: ports, - } - var rootfs *docker.RootFS - if builder.Docker.RootFS != nil { - rootfs = &docker.RootFS{ - Type: builder.Docker.RootFS.Type, - } - for _, id := range builder.Docker.RootFS.DiffIDs { - rootfs.Layers = append(rootfs.Layers, id.String()) - } - } - dImage := docker.Image{ - Parent: builder.FromImage, - ContainerConfig: dConfig, - Container: builder.Container, - Author: builder.Maintainer(), - Architecture: builder.Architecture(), - RootFS: rootfs, - } - dImage.Config = &dImage.ContainerConfig - err = ib.FromImage(&dImage, node) - if err != nil { - if err2 := builder.Delete(); err2 != nil { - logrus.Debugf("error deleting container which we failed to update: %v", err2) - } - return nil, errors.Wrapf(err, "error updating build context") - } - } - mountPoint, err := builder.Mount(builder.MountLabel) - if err != nil { - if err2 := builder.Delete(); err2 != nil { - logrus.Debugf("error deleting container which we failed to mount: %v", err2) - } - return nil, errors.Wrapf(err, "error mounting new container") - } - if rebase { - // Make this our "current" working container. - s.mountPoint = mountPoint - s.builder = builder - } - logrus.Debugln("Container ID:", builder.ContainerID) - return builder, nil -} - -// Delete deletes the stage's working container, if we have one. -func (s *StageExecutor) Delete() (err error) { - if s.builder != nil { - err = s.builder.Delete() - s.builder = nil - } - return err -} - -// resolveNameToImageRef creates a types.ImageReference for the output name in local storage -func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, error) { - imageRef, err := alltransports.ParseImageName(output) - if err != nil { - candidates, _, _, err := util.ResolveName(output, "", b.systemContext, b.store) - if err != nil { - return nil, errors.Wrapf(err, "error parsing target image name %q", output) - } - if len(candidates) == 0 { - return nil, errors.Errorf("error parsing target image name %q", output) - } - imageRef2, err2 := is.Transport.ParseStoreReference(b.store, candidates[0]) - if err2 != nil { - return nil, errors.Wrapf(err, "error parsing target image name %q", output) - } - return imageRef2, nil - } - return imageRef, nil -} - -// stepRequiresLayer indicates whether or not the step should be followed by -// committing a layer container when creating an intermediate image. -func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool { - switch strings.ToUpper(step.Command) { - case "ADD", "COPY", "RUN": - return true - } - return false -} - -// getImageRootfs checks for an image matching the passed-in name in local -// storage. If it isn't found, it pulls down a copy. Then, if we don't have a -// working container root filesystem based on the image, it creates one. Then -// it returns that root filesystem's location. -func (s *StageExecutor) getImageRootfs(ctx context.Context, stage imagebuilder.Stage, image string) (mountPoint string, err error) { - if builder, ok := s.executor.containerMap[image]; ok { - return builder.MountPoint, nil - } - builder, err := s.prepare(ctx, stage, image, false, false) - if err != nil { - return "", err - } - s.executor.containerMap[image] = builder - return builder.MountPoint, nil -} - -// Execute runs each of the steps in the stage's parsed tree, in turn. -func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, base string) (imgID string, ref reference.Canonical, err error) { - ib := stage.Builder - checkForLayers := s.executor.layers && s.executor.useCache - moreStages := s.index < s.stages-1 - lastStage := !moreStages - imageIsUsedLater := moreStages && (s.executor.baseMap[stage.Name] || s.executor.baseMap[fmt.Sprintf("%d", stage.Position)]) - rootfsIsUsedLater := moreStages && (s.executor.rootfsMap[stage.Name] || s.executor.rootfsMap[fmt.Sprintf("%d", stage.Position)]) - - // If the base image's name corresponds to the result of an earlier - // stage, substitute that image's ID for the base image's name here. - // If not, then go on assuming that it's just a regular image that's - // either in local storage, or one that we have to pull from a - // registry. - if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage { - base = stageImage - } - - // Create the (first) working container for this stage. Reinitializing - // the imagebuilder configuration may alter the list of steps we have, - // so take a snapshot of them *after* that. - if _, err := s.prepare(ctx, stage, base, true, true); err != nil { - return "", nil, err - } - children := stage.Node.Children - - // A helper function to only log "COMMIT" as an explicit step if it's - // the very last step of a (possibly multi-stage) build. - logCommit := func(output string, instruction int) { - moreInstructions := instruction < len(children)-1 - if moreInstructions || moreStages { - return - } - commitMessage := "COMMIT" - if output != "" { - commitMessage = fmt.Sprintf("%s %s", commitMessage, output) - } - logrus.Debugf(commitMessage) - if !s.executor.quiet { - s.executor.log(commitMessage) - } - } - logImageID := func(imgID string) { - if s.executor.iidfile == "" { - fmt.Fprintf(s.executor.out, "%s\n", imgID) - } - } - - if len(children) == 0 { - // There are no steps. - if s.builder.FromImageID == "" || s.executor.squash { - // We either don't have a base image, or we need to - // squash the contents of the base image. Whichever is - // the case, we need to commit() to create a new image. - logCommit(s.output, -1) - if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(nil), false, s.output); err != nil { - return "", nil, errors.Wrapf(err, "error committing base container") - } - } else { - // We don't need to squash the base image, so just - // reuse the base image. - logCommit(s.output, -1) - if imgID, ref, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil { - return "", nil, err - } - } - logImageID(imgID) - } - - for i, node := range children { - moreInstructions := i < len(children)-1 - lastInstruction := !moreInstructions - // Resolve any arguments in this instruction. - step := ib.Step() - if err := step.Resolve(node); err != nil { - return "", nil, errors.Wrapf(err, "error resolving step %+v", *node) - } - logrus.Debugf("Parsed Step: %+v", *step) - if !s.executor.quiet { - s.executor.log("%s", step.Original) - } - - // Check if there's a --from if the step command is COPY or - // ADD. Set copyFrom to point to either the context directory - // or the root of the container from the specified stage. - s.copyFrom = s.executor.contextDir - for _, n := range step.Flags { - command := strings.ToUpper(step.Command) - if strings.Contains(n, "--from") && (command == "COPY" || command == "ADD") { - var mountPoint string - arr := strings.Split(n, "=") - otherStage, ok := s.executor.stages[arr[1]] - if !ok { - if mountPoint, err = s.getImageRootfs(ctx, stage, arr[1]); err != nil { - return "", nil, errors.Errorf("%s --from=%s: no stage or image found with that name", command, arr[1]) - } - } else { - mountPoint = otherStage.mountPoint - } - s.copyFrom = mountPoint - break - } - } - - // Determine if there are any RUN instructions to be run after - // this step. If not, we won't have to bother preserving the - // contents of any volumes declared between now and when we - // finish. - noRunsRemaining := false - if moreInstructions { - noRunsRemaining = !ib.RequiresStart(&parser.Node{Children: children[i+1:]}) - } - - // If we're doing a single-layer build, just process the - // instruction. - if !s.executor.layers { - err := ib.Run(step, s, noRunsRemaining) - if err != nil { - logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) - return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) - } - if moreInstructions { - // There are still more instructions to process - // for this stage. Make a note of the - // instruction in the history that we'll write - // for the image when we eventually commit it. - now := time.Now() - s.builder.AddPrependedEmptyLayer(&now, s.executor.getCreatedBy(node), "", "") - continue - } else { - // This is the last instruction for this stage, - // so we should commit this container to create - // an image, but only if it's the last one, or - // if it's used as the basis for a later stage. - if lastStage || imageIsUsedLater { - logCommit(s.output, i) - imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), false, s.output) - if err != nil { - return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step) - } - logImageID(imgID) - } else { - imgID = "" - } - break - } - } - - // We're in a multi-layered build. - var ( - commitName string - cacheID string - err error - rebase bool - ) - - // 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 we're using the cache, and we've managed to stick with - // cached images so far, look for one that matches what we - // expect to produce for this instruction. - if checkForLayers && !(s.executor.squash && lastInstruction && lastStage) { - cacheID, err = s.layerExists(ctx, node, children[:i]) - if err != nil { - return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build") - } - if cacheID != "" { - // Note the cache hit. - fmt.Fprintf(s.executor.out, "--> Using cache %s\n", cacheID) - } else { - // We're not going to find any more cache hits. - checkForLayers = false - } - } - - if cacheID != "" { - // A suitable cached image was found, so just reuse it. - // If we need to name the resulting image because it's - // the last step in this stage, add the name to the - // image. - imgID = cacheID - if commitName != "" { - logCommit(commitName, i) - if imgID, ref, err = s.tagExistingImage(ctx, cacheID, commitName); err != nil { - return "", nil, err - } - logImageID(imgID) - } - // Update our working container to be based off of the - // cached image, if we might need to use it as a basis - // for the next instruction, or if we need the root - // filesystem to match the image contents for the sake - // of a later stage that wants to copy content from it. - rebase = moreInstructions || rootfsIsUsedLater - // 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 - // last cache image will be all that we need, since we - // still don't want to restart using the image's - // configuration blob. - if !s.stepRequiresLayer(step) { - err := ib.Run(step, s, noRunsRemaining) - if err != nil { - logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) - return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) - } - } - } else { - // If we didn't find a cached image that we could just reuse, - // process the instruction directly. - err := ib.Run(step, s, noRunsRemaining) - if err != nil { - logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) - return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) - } - // Create a new image, maybe with a new layer. - logCommit(s.output, i) - imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), !s.stepRequiresLayer(step), commitName) - if err != nil { - return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step) - } - logImageID(imgID) - // We only need to build a new container rootfs - // using this image if we plan on making - // further changes to it. Subsequent stages - // that just want to use the rootfs as a source - // for COPY or ADD will be content with what we - // already have. - rebase = moreInstructions - } - - if rebase { - // Since we either committed the working container or - // are about to replace it with one based on a cached - // image, add the current working container's ID to the - // list of successful intermediate containers that - // we'll clean up later. - s.containerIDs = append(s.containerIDs, s.builder.ContainerID) - - // Prepare for the next step or subsequent phases by - // creating a new working container with the - // just-committed or updated cached image as its new - // base image. - if _, err := s.prepare(ctx, stage, imgID, false, true); err != nil { - return "", nil, errors.Wrap(err, "error preparing container for next step") - } - } - } - - return imgID, ref, nil -} - -// tagExistingImage adds names to an image already in the store -func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) { - // If we don't need to attach a name to the image, just return the cache ID. - if output == "" { - return cacheID, nil, nil - } - - // Get the destination image reference. - dest, err := s.executor.resolveNameToImageRef(output) - if err != nil { - return "", nil, err - } - - policyContext, err := util.GetPolicyContext(s.executor.systemContext) - if err != nil { - return "", nil, err - } - defer policyContext.Destroy() - - // Look up the source image, expecting it to be in local storage - src, err := is.Transport.ParseStoreReference(s.executor.store, cacheID) - if err != nil { - return "", nil, errors.Wrapf(err, "error getting source imageReference for %q", cacheID) - } - manifestBytes, err := cp.Image(ctx, policyContext, dest, src, nil) - if err != nil { - return "", nil, errors.Wrapf(err, "error copying image %q", cacheID) - } - manifestDigest, err := manifest.Digest(manifestBytes) - if err != nil { - return "", nil, errors.Wrapf(err, "error computing digest of manifest for image %q", cacheID) - } - img, err := is.Transport.GetStoreImage(s.executor.store, dest) - if err != nil { - return "", nil, errors.Wrapf(err, "error locating new copy of image %q (i.e., %q)", cacheID, transports.ImageName(dest)) - } - var ref reference.Canonical - if dref := dest.DockerReference(); dref != nil { - if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { - return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q (i.e., %q)", cacheID, transports.ImageName(dest)) - } - } - return img.ID, ref, nil -} - -// layerExists 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) layerExists(ctx context.Context, currNode *parser.Node, children []*parser.Node) (string, error) { - // Get the list of images available in the image store - images, err := s.executor.store.Images() - if err != nil { - return "", errors.Wrap(err, "error getting image list from store") - } - var baseHistory []v1.History - if s.builder.FromImageID != "" { - baseHistory, err = s.executor.getImageHistory(ctx, s.builder.FromImageID) - if err != nil { - return "", errors.Wrapf(err, "error getting history of base image %q", s.builder.FromImageID) - } - } - for _, image := range images { - var imageTopLayer *storage.Layer - if image.TopLayer != "" { - imageTopLayer, err = s.executor.store.Layer(image.TopLayer) - if err != nil { - return "", errors.Wrapf(err, "error getting top layer info") - } - } - // If the parent of the top layer of an image is equal to the current build image's top layer, - // it means that this image is potentially a cached intermediate image from a previous - // build. Next we double check that the history of this image is equivalent to the previous - // lines in the Dockerfile up till the point we are at in the build. - if imageTopLayer == nil || (s.builder.TopLayer != "" && (imageTopLayer.Parent == s.builder.TopLayer || imageTopLayer.ID == s.builder.TopLayer)) { - history, err := s.executor.getImageHistory(ctx, image.ID) - if err != nil { - return "", errors.Wrapf(err, "error getting history of %q", image.ID) - } - // children + currNode is the point of the Dockerfile we are currently at. - if s.executor.historyMatches(baseHistory, currNode, history) { - // This checks if the files copied during build have been changed if the node is - // a COPY or ADD command. - filesMatch, err := s.copiedFilesMatch(currNode, history[len(history)-1].Created) - if err != nil { - return "", errors.Wrapf(err, "error checking if copied files match") - } - if filesMatch { - return image.ID, nil - } - } - } - } - return "", nil -} - -// getImageHistory returns the history of imageID. -func (b *Executor) getImageHistory(ctx context.Context, imageID string) ([]v1.History, error) { - imageRef, err := is.Transport.ParseStoreReference(b.store, "@"+imageID) - if err != nil { - return nil, errors.Wrapf(err, "error getting image reference %q", imageID) - } - ref, err := imageRef.NewImage(ctx, nil) - if err != nil { - return nil, errors.Wrapf(err, "error creating new image from reference to image %q", imageID) - } - defer ref.Close() - oci, err := ref.OCIConfig(ctx) - if err != nil { - return nil, errors.Wrapf(err, "error getting possibly-converted OCI config of image %q", imageID) - } - return oci.History, nil -} - -// getCreatedBy returns the command the image at node will be created by. -func (b *Executor) getCreatedBy(node *parser.Node) string { - if node == nil { - return "/bin/sh" - } - if node.Value == "run" { - buildArgs := b.getBuildArgs() - if buildArgs != "" { - return "|" + strconv.Itoa(len(strings.Split(buildArgs, " "))) + " " + buildArgs + " /bin/sh -c " + node.Original[4:] - } - return "/bin/sh -c " + node.Original[4:] - } - return "/bin/sh -c #(nop) " + node.Original -} - -// historyMatches returns true if a candidate history matches the history of our -// base image (if we have one), plus the current instruction. -// Used to verify whether a cache of the intermediate image exists and whether -// to run the build again. -func (b *Executor) historyMatches(baseHistory []v1.History, child *parser.Node, history []v1.History) bool { - if len(baseHistory) >= len(history) { - return false - } - if len(history)-len(baseHistory) != 1 { - return false - } - for i := range baseHistory { - if baseHistory[i].CreatedBy != history[i].CreatedBy { - return false - } - if baseHistory[i].Comment != history[i].Comment { - return false - } - if baseHistory[i].Author != history[i].Author { - return false - } - if baseHistory[i].EmptyLayer != history[i].EmptyLayer { - return false - } - if baseHistory[i].Created != nil && history[i].Created == nil { - return false - } - if baseHistory[i].Created == nil && history[i].Created != nil { - return false - } - if baseHistory[i].Created != nil && history[i].Created != nil && *baseHistory[i].Created != *history[i].Created { - return false - } - } - if history[len(baseHistory)].CreatedBy != b.getCreatedBy(child) { - return false - } - return true -} - -// getBuildArgs returns a string of the build-args specified during the build process -// it excludes any build-args that were not used in the build process -func (b *Executor) getBuildArgs() string { - var buildArgs []string - for k, v := range b.buildArgs { - if _, ok := b.unusedArgs[k]; !ok { - buildArgs = append(buildArgs, k+"="+v) - } - } - sort.Strings(buildArgs) - return strings.Join(buildArgs, " ") -} - -// getFilesToCopy goes through node to get all the src files that are copied, added or downloaded. -// It is possible for the Dockerfile to have src as hom*, which means all files that have hom as a prefix. -// Another format is hom?.txt, which means all files that have that name format with the ? replaced by another character. -func (s *StageExecutor) getFilesToCopy(node *parser.Node) ([]string, error) { - currNode := node.Next - var src []string - for currNode.Next != nil { - if strings.HasPrefix(currNode.Value, "http://") || strings.HasPrefix(currNode.Value, "https://") { - src = append(src, currNode.Value) - currNode = currNode.Next - continue - } - matches, err := filepath.Glob(filepath.Join(s.copyFrom, currNode.Value)) - if err != nil { - return nil, errors.Wrapf(err, "error finding match for pattern %q", currNode.Value) - } - src = append(src, matches...) - currNode = currNode.Next - } - return src, nil -} - -// copiedFilesMatch checks to see if the node instruction is a COPY or ADD. -// If it is either of those two it checks the timestamps on all the files copied/added -// by the dockerfile. If the host version has a time stamp greater than the time stamp -// of the build, the build will not use the cached version and will rebuild. -func (s *StageExecutor) copiedFilesMatch(node *parser.Node, historyTime *time.Time) (bool, error) { - if node.Value != "add" && node.Value != "copy" { - return true, nil - } - - src, err := s.getFilesToCopy(node) - if err != nil { - return false, err - } - for _, item := range src { - // for urls, check the Last-Modified field in the header. - if strings.HasPrefix(item, "http://") || strings.HasPrefix(item, "https://") { - urlContentNew, err := urlContentModified(item, historyTime) - if err != nil { - return false, err - } - if urlContentNew { - return false, nil - } - continue - } - // Walks the file tree for local files and uses chroot to ensure we don't escape out of the allowed path - // when resolving any symlinks. - // Change the time format to ensure we don't run into a parsing error when converting again from string - // to time.Time. It is a known Go issue that the conversions cause errors sometimes, so specifying a particular - // time format here when converting to a string. - timeIsGreater, err := resolveModifiedTime(s.copyFrom, item, historyTime.Format(time.RFC3339Nano)) - if err != nil { - return false, errors.Wrapf(err, "error resolving symlinks and comparing modified times: %q", item) - } - if timeIsGreater { - return false, nil - } - } - return true, nil -} - -// urlContentModified sends a get request to the url and checks if the header has a value in -// Last-Modified, and if it does compares the time stamp to that of the history of the cached image. -// returns true if there is no Last-Modified value in the header. -func urlContentModified(url string, historyTime *time.Time) (bool, error) { - resp, err := http.Get(url) - if err != nil { - return false, errors.Wrapf(err, "error getting %q", url) - } - if lastModified := resp.Header.Get("Last-Modified"); lastModified != "" { - lastModifiedTime, err := time.Parse(time.RFC1123, lastModified) - if err != nil { - return false, errors.Wrapf(err, "error parsing time for %q", url) - } - return lastModifiedTime.After(*historyTime), nil - } - logrus.Debugf("Response header did not have Last-Modified %q, will rebuild.", url) - return true, nil -} - -// commit writes the container's contents to an image, using a passed-in tag as -// the name if there is one, generating a unique ID-based one otherwise. -func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) { - var imageRef types.ImageReference - if output != "" { - imageRef2, err := s.executor.resolveNameToImageRef(output) - if err != nil { - return "", nil, err - } - imageRef = imageRef2 - } - - if ib.Author != "" { - s.builder.SetMaintainer(ib.Author) - } - config := ib.Config() - if createdBy != "" { - s.builder.SetCreatedBy(createdBy) - } - s.builder.SetHostname(config.Hostname) - s.builder.SetDomainname(config.Domainname) - s.builder.SetUser(config.User) - s.builder.ClearPorts() - for p := range config.ExposedPorts { - s.builder.SetPort(string(p)) - } - for _, envSpec := range config.Env { - spec := strings.SplitN(envSpec, "=", 2) - s.builder.SetEnv(spec[0], spec[1]) - } - s.builder.SetCmd(config.Cmd) - s.builder.ClearVolumes() - for v := range config.Volumes { - s.builder.AddVolume(v) - } - s.builder.ClearOnBuild() - for _, onBuildSpec := range config.OnBuild { - s.builder.SetOnBuild(onBuildSpec) - } - s.builder.SetWorkDir(config.WorkingDir) - s.builder.SetEntrypoint(config.Entrypoint) - s.builder.SetShell(config.Shell) - s.builder.SetStopSignal(config.StopSignal) - if config.Healthcheck != nil { - s.builder.SetHealthcheck(&buildahdocker.HealthConfig{ - Test: append([]string{}, config.Healthcheck.Test...), - Interval: config.Healthcheck.Interval, - Timeout: config.Healthcheck.Timeout, - StartPeriod: config.Healthcheck.StartPeriod, - Retries: config.Healthcheck.Retries, - }) - } else { - s.builder.SetHealthcheck(nil) - } - s.builder.ClearLabels() - for k, v := range config.Labels { - s.builder.SetLabel(k, v) - } - for _, labelSpec := range s.executor.labels { - label := strings.SplitN(labelSpec, "=", 2) - if len(label) > 1 { - s.builder.SetLabel(label[0], label[1]) - } else { - s.builder.SetLabel(label[0], "") - } - } - for _, annotationSpec := range s.executor.annotations { - annotation := strings.SplitN(annotationSpec, "=", 2) - if len(annotation) > 1 { - s.builder.SetAnnotation(annotation[0], annotation[1]) - } else { - s.builder.SetAnnotation(annotation[0], "") - } - } - if imageRef != nil { - logName := transports.ImageName(imageRef) - logrus.Debugf("COMMIT %q", logName) - } else { - logrus.Debugf("COMMIT") - } - writer := s.executor.reportWriter - if s.executor.layers || !s.executor.useCache { - writer = nil - } - options := buildah.CommitOptions{ - Compression: s.executor.compression, - SignaturePolicyPath: s.executor.signaturePolicyPath, - ReportWriter: writer, - PreferredManifestType: s.executor.outputFormat, - SystemContext: s.executor.systemContext, - Squash: s.executor.squash, - EmptyLayer: emptyLayer, - BlobDirectory: s.executor.blobDirectory, - } - imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) - if err != nil { - return "", nil, err - } - var ref reference.Canonical - if imageRef != nil { - if dref := imageRef.DockerReference(); dref != nil { - if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { - return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q", imgID) - } - } - } - return imgID, ref, nil -} - -// Build takes care of the details of running Prepare/Execute/Commit/Delete -// over each of the one or more parsed Dockerfiles and stages. -func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) { - if len(stages) == 0 { - return "", nil, errors.New("error building: no stages to build") - } - var cleanupImages []string - cleanupStages := make(map[int]*StageExecutor) - - cleanup := func() error { - var lastErr error - // Clean up any containers associated with the final container - // built by a stage, for stages that succeeded, since we no - // longer need their filesystem contents. - for _, stage := range cleanupStages { - if err := stage.Delete(); err != nil { - logrus.Debugf("Failed to cleanup stage containers: %v", err) - lastErr = err - } - } - cleanupStages = nil - // Clean up any builders that we used to get data from images. - for _, builder := range b.containerMap { - if err := builder.Delete(); err != nil { - logrus.Debugf("Failed to cleanup image containers: %v", err) - lastErr = err - } - } - b.containerMap = nil - // Clean up any intermediate containers associated with stages, - // since we're not keeping them for debugging. - if b.removeIntermediateCtrs { - if err := b.deleteSuccessfulIntermediateCtrs(); err != nil { - logrus.Debugf("Failed to cleanup intermediate containers: %v", err) - lastErr = err - } - } - // Remove images from stages except the last one, since we're - // not going to use them as a starting point for any new - // stages. - for i := range cleanupImages { - removeID := cleanupImages[len(cleanupImages)-i-1] - if removeID == imageID { - continue - } - if _, err := b.store.DeleteImage(removeID, true); err != nil { - logrus.Debugf("failed to remove intermediate image %q: %v", removeID, err) - if b.forceRmIntermediateCtrs || errors.Cause(err) != storage.ErrImageUsedByContainer { - lastErr = err - } - } - } - cleanupImages = nil - return lastErr - } - defer cleanup() - - // Build maps of every named base image and every referenced stage root - // filesystem. Individual stages can use them to determine whether or - // not they can skip certain steps near the end of their stages. - for _, stage := range stages { - node := stage.Node // first line - for node != nil { // each line - for _, child := range node.Children { // tokens on this line, though we only care about the first - switch strings.ToUpper(child.Value) { // first token - instruction - case "FROM": - if child.Next != nil { // second token on this line - base := child.Next.Value - if base != "scratch" { - // TODO: this didn't undergo variable and arg - // expansion, so if the AS clause in another - // FROM instruction uses argument values, - // we might not record the right value here. - b.baseMap[base] = true - logrus.Debugf("base: %q", base) - } - } - case "ADD", "COPY": - for _, flag := range child.Flags { // flags for this instruction - if strings.HasPrefix(flag, "--from=") { - // TODO: this didn't undergo variable and - // arg expansion, so if the previous stage - // was named using argument values, we might - // not record the right value here. - rootfs := flag[7:] - b.rootfsMap[rootfs] = true - logrus.Debugf("rootfs: %q", rootfs) - } - } - } - break - } - node = node.Next // next line - } - } - - // Run through the build stages, one at a time. - for stageIndex, stage := range stages { - var lastErr error - - ib := stage.Builder - node := stage.Node - base, err := ib.From(node) - if err != nil { - logrus.Debugf("Build(node.Children=%#v)", node.Children) - return "", nil, err - } - - // If this is the last stage, then the image that we produce at - // its end should be given the desired output name. - output := "" - if stageIndex == len(stages)-1 { - output = b.output - } - - stageExecutor := b.startStage(stage.Name, stage.Position, len(stages), base, output) - - // If this a single-layer build, or if it's a multi-layered - // build and b.forceRmIntermediateCtrs is set, make sure we - // remove the intermediate/build containers, regardless of - // whether or not the stage's build fails. - if b.forceRmIntermediateCtrs || !b.layers { - cleanupStages[stage.Position] = stageExecutor - } - - // Build this stage. - if imageID, ref, err = stageExecutor.Execute(ctx, stage, base); err != nil { - lastErr = err - } - if lastErr != nil { - return "", nil, lastErr - } - - // The stage succeeded, so remove its build container if we're - // told to delete successful intermediate/build containers for - // multi-layered builds. - if b.removeIntermediateCtrs { - cleanupStages[stage.Position] = stageExecutor - } - - // If this is an intermediate stage, make a note of the ID, so - // that we can look it up later. - if stageIndex < len(stages)-1 && imageID != "" { - b.imageMap[stage.Name] = imageID - // We're not populating the cache with intermediate - // images, so add this one to the list of images that - // we'll remove later. - if !b.layers { - cleanupImages = append(cleanupImages, imageID) - } - imageID = "" - } - } - - if len(b.unusedArgs) > 0 { - unusedList := make([]string, 0, len(b.unusedArgs)) - for k := range b.unusedArgs { - unusedList = append(unusedList, k) - } - sort.Strings(unusedList) - fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList) - } - - if len(b.additionalTags) > 0 { - if dest, err := b.resolveNameToImageRef(b.output); err == nil { - switch dest.Transport().Name() { - case is.Transport.Name(): - img, err := is.Transport.GetStoreImage(b.store, dest) - if err != nil { - return imageID, ref, errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest)) - } - if err = util.AddImageNames(b.store, "", b.systemContext, img, b.additionalTags); err != nil { - return imageID, ref, errors.Wrapf(err, "error setting image names to %v", append(img.Names, b.additionalTags...)) - } - logrus.Debugf("assigned names %v to image %q", img.Names, img.ID) - default: - logrus.Warnf("don't know how to add tags to images stored in %q transport", dest.Transport().Name()) - } - } - } - - if err := cleanup(); err != nil { - return "", nil, err - } - - if b.iidfile != "" { - if err = ioutil.WriteFile(b.iidfile, []byte(imageID), 0644); err != nil { - return imageID, ref, errors.Wrapf(err, "failed to write image ID to file %q", b.iidfile) - } - } - - return imageID, ref, nil -} - // BuildDockerfiles parses a set of one or more Dockerfiles (which may be // URLs), creates a new Executor, and then runs Prepare/Execute/Commit/Delete // over the entire set of instructions. @@ -1803,10 +192,10 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt dinfo, err := os.Stat(dfile) if os.IsNotExist(err) { dfile = filepath.Join(options.ContextDirectory, dfile) - } - dinfo, err = os.Stat(dfile) - if err != nil { - return "", nil, errors.Wrapf(err, "error reading info about %q", dfile) + dinfo, err = os.Stat(dfile) + if err != nil { + return "", nil, errors.Wrapf(err, "error reading info about %q", dfile) + } } // If given a directory, add '/Dockerfile' to it. if dinfo.Mode().IsDir() { @@ -1871,39 +260,6 @@ func BuildDockerfiles(ctx context.Context, store storage.Store, options BuildOpt return exec.Build(ctx, stages) } -// deleteSuccessfulIntermediateCtrs goes through the container IDs in each -// stage's containerIDs list and deletes the containers associated with those -// IDs. -func (b *Executor) deleteSuccessfulIntermediateCtrs() error { - var lastErr error - for _, s := range b.stages { - for _, ctr := range s.containerIDs { - if err := b.store.DeleteContainer(ctr); err != nil { - logrus.Errorf("error deleting build container %q: %v\n", ctr, err) - lastErr = err - } - } - // The stages map includes some stages under multiple keys, so - // clearing their lists after we process a given stage is - // necessary to avoid triggering errors that would occur if we - // tried to delete a given stage's containers multiple times. - s.containerIDs = nil - } - return lastErr -} - -func (s *StageExecutor) EnsureContainerPath(path string) error { - targetPath, err := securejoin.SecureJoin(s.mountPoint, path) - _, err = os.Lstat(targetPath) - if err != nil && os.IsNotExist(err) { - err = os.MkdirAll(targetPath, 0755) - } - if err != nil { - return errors.Wrapf(err, "error ensuring container path %q", path) - } - return nil -} - // preprocessDockerfileContents runs CPP(1) in preprocess-only mode on the input // dockerfile content and will use ctxDir as the base include path. // diff --git a/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink_linux.go b/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink_linux.go index e9d745b67..a6d3b4c8f 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink_linux.go +++ b/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink_linux.go @@ -209,7 +209,7 @@ func getSymbolicLink(path string) (string, error) { } // if isSymlink is true, check if resolvedPath is potentially another symlink // keep doing this till resolvedPath is not a symlink and isSymlink is false - for isSymlink == true { + for isSymlink { // Need to keep track of number of symlinks resolved // Will also return an error if the symlink points to itself as that will exceed maxSymlinksResolved if symLinksResolved >= maxSymlinksResolved { diff --git a/vendor/github.com/containers/buildah/imagebuildah/executor.go b/vendor/github.com/containers/buildah/imagebuildah/executor.go new file mode 100644 index 000000000..8d68fe85f --- /dev/null +++ b/vendor/github.com/containers/buildah/imagebuildah/executor.go @@ -0,0 +1,538 @@ +package imagebuildah + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "os" + "sort" + "strconv" + "strings" + + "github.com/containers/buildah" + "github.com/containers/buildah/util" + "github.com/containers/image/docker/reference" + is "github.com/containers/image/storage" + "github.com/containers/image/transports" + "github.com/containers/image/transports/alltransports" + "github.com/containers/image/types" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/openshift/imagebuilder" + "github.com/openshift/imagebuilder/dockerfile/parser" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// builtinAllowedBuildArgs is list of built-in allowed build args. Normally we +// complain if we're given values for arguments which have no corresponding ARG +// instruction in the Dockerfile, since that's usually an indication of a user +// error, but for these values we make exceptions and ignore them. +var builtinAllowedBuildArgs = map[string]bool{ + "HTTP_PROXY": true, + "http_proxy": true, + "HTTPS_PROXY": true, + "https_proxy": true, + "FTP_PROXY": true, + "ftp_proxy": true, + "NO_PROXY": true, + "no_proxy": true, +} + +// Executor is a buildah-based implementation of the imagebuilder.Executor +// interface. It coordinates the entire build by using one StageExecutors to +// handle each stage of the build. +type Executor struct { + stages map[string]*StageExecutor + store storage.Store + contextDir string + pullPolicy buildah.PullPolicy + registry string + ignoreUnrecognizedInstructions bool + quiet bool + runtime string + runtimeArgs []string + transientMounts []Mount + compression archive.Compression + output string + outputFormat string + additionalTags []string + log func(format string, args ...interface{}) + in io.Reader + out io.Writer + err io.Writer + signaturePolicyPath string + systemContext *types.SystemContext + reportWriter io.Writer + isolation buildah.Isolation + namespaceOptions []buildah.NamespaceOption + configureNetwork buildah.NetworkConfigurationPolicy + cniPluginPath string + cniConfigDir string + idmappingOptions *buildah.IDMappingOptions + commonBuildOptions *buildah.CommonBuildOptions + defaultMountsFilePath string + iidfile string + squash bool + labels []string + annotations []string + layers bool + useCache bool + removeIntermediateCtrs bool + forceRmIntermediateCtrs bool + imageMap map[string]string // Used to map images that we create to handle the AS construct. + containerMap map[string]*buildah.Builder // Used to map from image names to only-created-for-the-rootfs containers. + baseMap map[string]bool // Holds the names of every base image, as given. + rootfsMap map[string]bool // Holds the names of every stage whose rootfs is referenced in a COPY or ADD instruction. + blobDirectory string + excludes []string + unusedArgs map[string]struct{} + buildArgs map[string]string +} + +// NewExecutor creates a new instance of the imagebuilder.Executor interface. +func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Node) (*Executor, error) { + excludes, err := imagebuilder.ParseDockerignore(options.ContextDirectory) + if err != nil { + return nil, err + } + + exec := Executor{ + store: store, + contextDir: options.ContextDirectory, + excludes: excludes, + pullPolicy: options.PullPolicy, + registry: options.Registry, + ignoreUnrecognizedInstructions: options.IgnoreUnrecognizedInstructions, + quiet: options.Quiet, + runtime: options.Runtime, + runtimeArgs: options.RuntimeArgs, + transientMounts: options.TransientMounts, + compression: options.Compression, + output: options.Output, + outputFormat: options.OutputFormat, + additionalTags: options.AdditionalTags, + signaturePolicyPath: options.SignaturePolicyPath, + systemContext: options.SystemContext, + log: options.Log, + in: options.In, + out: options.Out, + err: options.Err, + reportWriter: options.ReportWriter, + isolation: options.Isolation, + namespaceOptions: options.NamespaceOptions, + configureNetwork: options.ConfigureNetwork, + cniPluginPath: options.CNIPluginPath, + cniConfigDir: options.CNIConfigDir, + idmappingOptions: options.IDMappingOptions, + commonBuildOptions: options.CommonBuildOpts, + defaultMountsFilePath: options.DefaultMountsFilePath, + iidfile: options.IIDFile, + squash: options.Squash, + labels: append([]string{}, options.Labels...), + annotations: append([]string{}, options.Annotations...), + layers: options.Layers, + useCache: !options.NoCache, + removeIntermediateCtrs: options.RemoveIntermediateCtrs, + forceRmIntermediateCtrs: options.ForceRmIntermediateCtrs, + imageMap: make(map[string]string), + containerMap: make(map[string]*buildah.Builder), + baseMap: make(map[string]bool), + rootfsMap: make(map[string]bool), + blobDirectory: options.BlobDirectory, + unusedArgs: make(map[string]struct{}), + buildArgs: options.Args, + } + if exec.err == nil { + exec.err = os.Stderr + } + if exec.out == nil { + exec.out = os.Stdout + } + if exec.log == nil { + stepCounter := 0 + exec.log = func(format string, args ...interface{}) { + stepCounter++ + prefix := fmt.Sprintf("STEP %d: ", stepCounter) + suffix := "\n" + fmt.Fprintf(exec.err, prefix+format+suffix, args...) + } + } + for arg := range options.Args { + if _, isBuiltIn := builtinAllowedBuildArgs[arg]; !isBuiltIn { + exec.unusedArgs[arg] = struct{}{} + } + } + for _, line := range mainNode.Children { + node := line + for node != nil { // tokens on this line, though we only care about the first + switch strings.ToUpper(node.Value) { // first token - instruction + case "ARG": + arg := node.Next + if arg != nil { + // We have to be careful here - it's either an argument + // and value, or just an argument, since they can be + // separated by either "=" or whitespace. + list := strings.SplitN(arg.Value, "=", 2) + if _, stillUnused := exec.unusedArgs[list[0]]; stillUnused { + delete(exec.unusedArgs, list[0]) + } + } + } + break + } + } + return &exec, nil +} + +// startStage creates a new stage executor that will be referenced whenever a +// COPY or ADD statement uses a --from=NAME flag. +func (b *Executor) startStage(name string, index, stages int, from, output string) *StageExecutor { + if b.stages == nil { + b.stages = make(map[string]*StageExecutor) + } + stage := &StageExecutor{ + executor: b, + index: index, + stages: stages, + name: name, + volumeCache: make(map[string]string), + volumeCacheInfo: make(map[string]os.FileInfo), + output: output, + } + b.stages[name] = stage + b.stages[from] = stage + if idx := strconv.Itoa(index); idx != name { + b.stages[idx] = stage + } + return stage +} + +// resolveNameToImageRef creates a types.ImageReference for the output name in local storage +func (b *Executor) resolveNameToImageRef(output string) (types.ImageReference, error) { + imageRef, err := alltransports.ParseImageName(output) + if err != nil { + candidates, _, _, err := util.ResolveName(output, "", b.systemContext, b.store) + if err != nil { + return nil, errors.Wrapf(err, "error parsing target image name %q", output) + } + if len(candidates) == 0 { + return nil, errors.Errorf("error parsing target image name %q", output) + } + imageRef2, err2 := is.Transport.ParseStoreReference(b.store, candidates[0]) + if err2 != nil { + return nil, errors.Wrapf(err, "error parsing target image name %q", output) + } + return imageRef2, nil + } + return imageRef, nil +} + +// getImageHistory returns the history of imageID. +func (b *Executor) getImageHistory(ctx context.Context, imageID string) ([]v1.History, error) { + imageRef, err := is.Transport.ParseStoreReference(b.store, "@"+imageID) + if err != nil { + return nil, errors.Wrapf(err, "error getting image reference %q", imageID) + } + ref, err := imageRef.NewImage(ctx, nil) + if err != nil { + return nil, errors.Wrapf(err, "error creating new image from reference to image %q", imageID) + } + defer ref.Close() + oci, err := ref.OCIConfig(ctx) + if err != nil { + return nil, errors.Wrapf(err, "error getting possibly-converted OCI config of image %q", imageID) + } + return oci.History, nil +} + +// getCreatedBy returns the command the image at node will be created by. +func (b *Executor) getCreatedBy(node *parser.Node) string { + if node == nil { + return "/bin/sh" + } + if node.Value == "run" { + buildArgs := b.getBuildArgs() + if buildArgs != "" { + return "|" + strconv.Itoa(len(strings.Split(buildArgs, " "))) + " " + buildArgs + " /bin/sh -c " + node.Original[4:] + } + return "/bin/sh -c " + node.Original[4:] + } + return "/bin/sh -c #(nop) " + node.Original +} + +// historyMatches returns true if a candidate history matches the history of our +// base image (if we have one), plus the current instruction. +// Used to verify whether a cache of the intermediate image exists and whether +// to run the build again. +func (b *Executor) historyMatches(baseHistory []v1.History, child *parser.Node, history []v1.History) bool { + if len(baseHistory) >= len(history) { + return false + } + if len(history)-len(baseHistory) != 1 { + return false + } + for i := range baseHistory { + if baseHistory[i].CreatedBy != history[i].CreatedBy { + return false + } + if baseHistory[i].Comment != history[i].Comment { + return false + } + if baseHistory[i].Author != history[i].Author { + return false + } + if baseHistory[i].EmptyLayer != history[i].EmptyLayer { + return false + } + if baseHistory[i].Created != nil && history[i].Created == nil { + return false + } + if baseHistory[i].Created == nil && history[i].Created != nil { + return false + } + if baseHistory[i].Created != nil && history[i].Created != nil && *baseHistory[i].Created != *history[i].Created { + return false + } + } + return history[len(baseHistory)].CreatedBy == b.getCreatedBy(child) +} + +// getBuildArgs returns a string of the build-args specified during the build process +// it excludes any build-args that were not used in the build process +func (b *Executor) getBuildArgs() string { + var buildArgs []string + for k, v := range b.buildArgs { + if _, ok := b.unusedArgs[k]; !ok { + buildArgs = append(buildArgs, k+"="+v) + } + } + sort.Strings(buildArgs) + return strings.Join(buildArgs, " ") +} + +// Build takes care of the details of running Prepare/Execute/Commit/Delete +// over each of the one or more parsed Dockerfiles and stages. +func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (imageID string, ref reference.Canonical, err error) { + if len(stages) == 0 { + return "", nil, errors.New("error building: no stages to build") + } + var cleanupImages []string + cleanupStages := make(map[int]*StageExecutor) + + cleanup := func() error { + var lastErr error + // Clean up any containers associated with the final container + // built by a stage, for stages that succeeded, since we no + // longer need their filesystem contents. + for _, stage := range cleanupStages { + if err := stage.Delete(); err != nil { + logrus.Debugf("Failed to cleanup stage containers: %v", err) + lastErr = err + } + } + cleanupStages = nil + // Clean up any builders that we used to get data from images. + for _, builder := range b.containerMap { + if err := builder.Delete(); err != nil { + logrus.Debugf("Failed to cleanup image containers: %v", err) + lastErr = err + } + } + b.containerMap = nil + // Clean up any intermediate containers associated with stages, + // since we're not keeping them for debugging. + if b.removeIntermediateCtrs { + if err := b.deleteSuccessfulIntermediateCtrs(); err != nil { + logrus.Debugf("Failed to cleanup intermediate containers: %v", err) + lastErr = err + } + } + // Remove images from stages except the last one, since we're + // not going to use them as a starting point for any new + // stages. + for i := range cleanupImages { + removeID := cleanupImages[len(cleanupImages)-i-1] + if removeID == imageID { + continue + } + if _, err := b.store.DeleteImage(removeID, true); err != nil { + logrus.Debugf("failed to remove intermediate image %q: %v", removeID, err) + if b.forceRmIntermediateCtrs || errors.Cause(err) != storage.ErrImageUsedByContainer { + lastErr = err + } + } + } + cleanupImages = nil + return lastErr + } + + defer func() { + if cleanupErr := cleanup(); cleanupErr != nil { + if err == nil { + err = cleanupErr + } else { + err = errors.Wrap(err, cleanupErr.Error()) + } + } + }() + + // Build maps of every named base image and every referenced stage root + // filesystem. Individual stages can use them to determine whether or + // not they can skip certain steps near the end of their stages. + for _, stage := range stages { + node := stage.Node // first line + for node != nil { // each line + for _, child := range node.Children { // tokens on this line, though we only care about the first + switch strings.ToUpper(child.Value) { // first token - instruction + case "FROM": + if child.Next != nil { // second token on this line + base := child.Next.Value + if base != "scratch" { + // TODO: this didn't undergo variable and arg + // expansion, so if the AS clause in another + // FROM instruction uses argument values, + // we might not record the right value here. + b.baseMap[base] = true + logrus.Debugf("base: %q", base) + } + } + case "ADD", "COPY": + for _, flag := range child.Flags { // flags for this instruction + if strings.HasPrefix(flag, "--from=") { + // TODO: this didn't undergo variable and + // arg expansion, so if the previous stage + // was named using argument values, we might + // not record the right value here. + rootfs := flag[7:] + b.rootfsMap[rootfs] = true + logrus.Debugf("rootfs: %q", rootfs) + } + } + } + break + } + node = node.Next // next line + } + } + + // Run through the build stages, one at a time. + for stageIndex, stage := range stages { + var lastErr error + + ib := stage.Builder + node := stage.Node + base, err := ib.From(node) + if err != nil { + logrus.Debugf("Build(node.Children=%#v)", node.Children) + return "", nil, err + } + + // If this is the last stage, then the image that we produce at + // its end should be given the desired output name. + output := "" + if stageIndex == len(stages)-1 { + output = b.output + } + + stageExecutor := b.startStage(stage.Name, stage.Position, len(stages), base, output) + + // If this a single-layer build, or if it's a multi-layered + // build and b.forceRmIntermediateCtrs is set, make sure we + // remove the intermediate/build containers, regardless of + // whether or not the stage's build fails. + if b.forceRmIntermediateCtrs || !b.layers { + cleanupStages[stage.Position] = stageExecutor + } + + // Build this stage. + if imageID, ref, err = stageExecutor.Execute(ctx, stage, base); err != nil { + lastErr = err + } + if lastErr != nil { + return "", nil, lastErr + } + + // The stage succeeded, so remove its build container if we're + // told to delete successful intermediate/build containers for + // multi-layered builds. + if b.removeIntermediateCtrs { + cleanupStages[stage.Position] = stageExecutor + } + + // If this is an intermediate stage, make a note of the ID, so + // that we can look it up later. + if stageIndex < len(stages)-1 && imageID != "" { + b.imageMap[stage.Name] = imageID + // We're not populating the cache with intermediate + // images, so add this one to the list of images that + // we'll remove later. + if !b.layers { + cleanupImages = append(cleanupImages, imageID) + } + imageID = "" + } + } + + if len(b.unusedArgs) > 0 { + unusedList := make([]string, 0, len(b.unusedArgs)) + for k := range b.unusedArgs { + unusedList = append(unusedList, k) + } + sort.Strings(unusedList) + fmt.Fprintf(b.out, "[Warning] one or more build args were not consumed: %v\n", unusedList) + } + + if len(b.additionalTags) > 0 { + if dest, err := b.resolveNameToImageRef(b.output); err == nil { + switch dest.Transport().Name() { + case is.Transport.Name(): + img, err := is.Transport.GetStoreImage(b.store, dest) + if err != nil { + return imageID, ref, errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest)) + } + if err = util.AddImageNames(b.store, "", b.systemContext, img, b.additionalTags); err != nil { + return imageID, ref, errors.Wrapf(err, "error setting image names to %v", append(img.Names, b.additionalTags...)) + } + logrus.Debugf("assigned names %v to image %q", img.Names, img.ID) + default: + logrus.Warnf("don't know how to add tags to images stored in %q transport", dest.Transport().Name()) + } + } + } + + if err := cleanup(); err != nil { + return "", nil, err + } + + if b.iidfile != "" { + if err = ioutil.WriteFile(b.iidfile, []byte(imageID), 0644); err != nil { + return imageID, ref, errors.Wrapf(err, "failed to write image ID to file %q", b.iidfile) + } + } + + return imageID, ref, nil +} + +// deleteSuccessfulIntermediateCtrs goes through the container IDs in each +// stage's containerIDs list and deletes the containers associated with those +// IDs. +func (b *Executor) deleteSuccessfulIntermediateCtrs() error { + var lastErr error + for _, s := range b.stages { + for _, ctr := range s.containerIDs { + if err := b.store.DeleteContainer(ctr); err != nil { + logrus.Errorf("error deleting build container %q: %v\n", ctr, err) + lastErr = err + } + } + // The stages map includes some stages under multiple keys, so + // clearing their lists after we process a given stage is + // necessary to avoid triggering errors that would occur if we + // tried to delete a given stage's containers multiple times. + s.containerIDs = nil + } + return lastErr +} diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go new file mode 100644 index 000000000..8030e351a --- /dev/null +++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go @@ -0,0 +1,1156 @@ +package imagebuildah + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/containers/buildah" + buildahdocker "github.com/containers/buildah/docker" + "github.com/containers/buildah/util" + cp "github.com/containers/image/copy" + "github.com/containers/image/docker/reference" + "github.com/containers/image/manifest" + is "github.com/containers/image/storage" + "github.com/containers/image/transports" + "github.com/containers/image/types" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + securejoin "github.com/cyphar/filepath-securejoin" + docker "github.com/fsouza/go-dockerclient" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/openshift/imagebuilder" + "github.com/openshift/imagebuilder/dockerfile/parser" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// StageExecutor bundles up what we need to know when executing one stage of a +// (possibly multi-stage) build. +// Each stage may need to produce an image to be used as the base in a later +// stage (with the last stage's image being the end product of the build), and +// it may need to leave its working container in place so that the container's +// root filesystem's contents can be used as the source for a COPY instruction +// in a later stage. +// Each stage has its own base image, so it starts with its own configuration +// and set of volumes. +// If we're naming the result of the build, only the last stage will apply that +// name to the image that it produces. +type StageExecutor struct { + executor *Executor + index int + stages int + name string + builder *buildah.Builder + preserved int + volumes imagebuilder.VolumeSet + volumeCache map[string]string + volumeCacheInfo map[string]os.FileInfo + mountPoint string + copyFrom string // Used to keep track of the --from flag from COPY and ADD + output string + containerIDs []string +} + +// Preserve informs the stage executor that from this point on, it needs to +// ensure that only COPY and ADD instructions can modify the contents of this +// directory or anything below it. +// The StageExecutor handles this by caching the contents of directories which +// have been marked this way before executing a RUN instruction, invalidating +// that cache when an ADD or COPY instruction sets any location under the +// directory as the destination, and using the cache to reset the contents of +// the directory tree after processing each RUN instruction. +// It would be simpler if we could just mark the directory as a read-only bind +// mount of itself during Run(), but the directory is expected to be remain +// writeable while the RUN instruction is being handled, even if any changes +// made within the directory are ultimately discarded. +func (s *StageExecutor) Preserve(path string) error { + logrus.Debugf("PRESERVE %q", path) + if s.volumes.Covers(path) { + // This path is already a subdirectory of a volume path that + // we're already preserving, so there's nothing new to be done + // except ensure that it exists. + archivedPath := filepath.Join(s.mountPoint, path) + if err := os.MkdirAll(archivedPath, 0755); err != nil { + return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) + } + if err := s.volumeCacheInvalidate(path); err != nil { + return errors.Wrapf(err, "error ensuring volume path %q is preserved", archivedPath) + } + return nil + } + // Figure out where the cache for this volume would be stored. + s.preserved++ + cacheDir, err := s.executor.store.ContainerDirectory(s.builder.ContainerID) + if err != nil { + return errors.Errorf("unable to locate temporary directory for container") + } + cacheFile := filepath.Join(cacheDir, fmt.Sprintf("volume%d.tar", s.preserved)) + // Save info about the top level of the location that we'll be archiving. + var archivedPath string + + // Try and resolve the symlink (if one exists) + // Set archivedPath and path based on whether a symlink is found or not + if symLink, err := resolveSymlink(s.mountPoint, path); err == nil { + archivedPath = filepath.Join(s.mountPoint, symLink) + path = symLink + } else { + return errors.Wrapf(err, "error reading symbolic link to %q", path) + } + + st, err := os.Stat(archivedPath) + if os.IsNotExist(err) { + if err = os.MkdirAll(archivedPath, 0755); err != nil { + return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) + } + st, err = os.Stat(archivedPath) + } + if err != nil { + logrus.Debugf("error reading info about %q: %v", archivedPath, err) + return errors.Wrapf(err, "error reading info about volume path %q", archivedPath) + } + s.volumeCacheInfo[path] = st + if !s.volumes.Add(path) { + // This path is not a subdirectory of a volume path that we're + // already preserving, so adding it to the list should work. + return errors.Errorf("error adding %q to the volume cache", path) + } + s.volumeCache[path] = cacheFile + // Now prune cache files for volumes that are now supplanted by this one. + removed := []string{} + for cachedPath := range s.volumeCache { + // Walk our list of cached volumes, and check that they're + // still in the list of locations that we need to cache. + found := false + for _, volume := range s.volumes { + if volume == cachedPath { + // We need to keep this volume's cache. + found = true + break + } + } + if !found { + // We don't need to keep this volume's cache. Make a + // note to remove it. + removed = append(removed, cachedPath) + } + } + // Actually remove the caches that we decided to remove. + for _, cachedPath := range removed { + 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) { + continue + } + return errors.Wrapf(err, "error removing %q", s.volumeCache[cachedPath]) + } + delete(s.volumeCache, cachedPath) + } + return nil +} + +// Remove any volume cache item which will need to be re-saved because we're +// writing to part of it. +func (s *StageExecutor) volumeCacheInvalidate(path string) error { + invalidated := []string{} + for cachedPath := range s.volumeCache { + if strings.HasPrefix(path, cachedPath+string(os.PathSeparator)) { + invalidated = append(invalidated, cachedPath) + } + } + for _, cachedPath := range invalidated { + if err := os.Remove(s.volumeCache[cachedPath]); err != nil { + if os.IsNotExist(err) { + continue + } + return errors.Wrapf(err, "error removing volume cache %q", s.volumeCache[cachedPath]) + } + archivedPath := filepath.Join(s.mountPoint, cachedPath) + logrus.Debugf("invalidated volume cache for %q from %q", archivedPath, s.volumeCache[cachedPath]) + delete(s.volumeCache, cachedPath) + } + return nil +} + +// Save the contents of each of the executor's list of volumes for which we +// don't already have a cache file. +func (s *StageExecutor) volumeCacheSave() error { + for cachedPath, cacheFile := range s.volumeCache { + archivedPath := filepath.Join(s.mountPoint, cachedPath) + _, err := os.Stat(cacheFile) + if err == nil { + logrus.Debugf("contents of volume %q are already cached in %q", archivedPath, cacheFile) + continue + } + if !os.IsNotExist(err) { + return errors.Wrapf(err, "error checking for cache of %q in %q", archivedPath, cacheFile) + } + if err := os.MkdirAll(archivedPath, 0755); err != nil { + return errors.Wrapf(err, "error ensuring volume path %q exists", archivedPath) + } + logrus.Debugf("caching contents of volume %q in %q", archivedPath, cacheFile) + cache, err := os.Create(cacheFile) + if err != nil { + return errors.Wrapf(err, "error creating archive at %q", cacheFile) + } + defer cache.Close() + rc, err := archive.Tar(archivedPath, archive.Uncompressed) + if err != nil { + return errors.Wrapf(err, "error archiving %q", archivedPath) + } + defer rc.Close() + _, err = io.Copy(cache, rc) + if err != nil { + return errors.Wrapf(err, "error archiving %q to %q", archivedPath, cacheFile) + } + } + return nil +} + +// Restore the contents of each of the executor's list of volumes. +func (s *StageExecutor) volumeCacheRestore() error { + for cachedPath, cacheFile := range s.volumeCache { + archivedPath := filepath.Join(s.mountPoint, cachedPath) + logrus.Debugf("restoring contents of volume %q from %q", archivedPath, cacheFile) + cache, err := os.Open(cacheFile) + if err != nil { + return errors.Wrapf(err, "error opening archive at %q", cacheFile) + } + defer cache.Close() + if err := os.RemoveAll(archivedPath); err != nil { + return errors.Wrapf(err, "error clearing volume path %q", archivedPath) + } + if err := os.MkdirAll(archivedPath, 0755); err != nil { + return errors.Wrapf(err, "error recreating volume path %q", archivedPath) + } + err = archive.Untar(cache, archivedPath, nil) + if err != nil { + return errors.Wrapf(err, "error extracting archive at %q", archivedPath) + } + if st, ok := s.volumeCacheInfo[cachedPath]; ok { + if err := os.Chmod(archivedPath, st.Mode()); err != nil { + return errors.Wrapf(err, "error restoring permissions on %q", archivedPath) + } + if err := os.Chown(archivedPath, 0, 0); err != nil { + return errors.Wrapf(err, "error setting ownership on %q", archivedPath) + } + if err := os.Chtimes(archivedPath, st.ModTime(), st.ModTime()); err != nil { + return errors.Wrapf(err, "error restoring datestamps on %q", archivedPath) + } + } + } + return nil +} + +// Copy copies data into the working tree. The "Download" field is how +// imagebuilder tells us the instruction was "ADD" and not "COPY" +func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) error { + for _, copy := range copies { + // Check the file and see if part of it is a symlink. + // Convert it to the target if so. To be ultrasafe + // do the same for the mountpoint. + hadFinalPathSeparator := len(copy.Dest) > 0 && copy.Dest[len(copy.Dest)-1] == os.PathSeparator + secureMountPoint, err := securejoin.SecureJoin("", s.mountPoint) + if err != nil { + return errors.Wrapf(err, "error resolving symlinks for copy destination %s", copy.Dest) + } + finalPath, err := securejoin.SecureJoin(secureMountPoint, copy.Dest) + if err != nil { + return errors.Wrapf(err, "error resolving symlinks for copy destination %s", copy.Dest) + } + if !strings.HasPrefix(finalPath, secureMountPoint) { + return errors.Wrapf(err, "error resolving copy destination %s", copy.Dest) + } + copy.Dest = strings.TrimPrefix(finalPath, secureMountPoint) + if len(copy.Dest) == 0 || copy.Dest[len(copy.Dest)-1] != os.PathSeparator { + if hadFinalPathSeparator { + copy.Dest += string(os.PathSeparator) + } + } + + if copy.Download { + logrus.Debugf("ADD %#v, %#v", excludes, copy) + } else { + logrus.Debugf("COPY %#v, %#v", excludes, copy) + } + if err := s.volumeCacheInvalidate(copy.Dest); err != nil { + return err + } + sources := []string{} + for _, src := range copy.Src { + contextDir := s.executor.contextDir + copyExcludes := excludes + var idMappingOptions *buildah.IDMappingOptions + if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") { + sources = append(sources, src) + } else if len(copy.From) > 0 { + var srcRoot string + if other, ok := s.executor.stages[copy.From]; ok && other.index < s.index { + srcRoot = other.mountPoint + contextDir = other.mountPoint + idMappingOptions = &other.builder.IDMappingOptions + } else if builder, ok := s.executor.containerMap[copy.From]; ok { + srcRoot = builder.MountPoint + contextDir = builder.MountPoint + idMappingOptions = &builder.IDMappingOptions + } else { + return errors.Errorf("the stage %q has not been built", copy.From) + } + srcSecure, err := securejoin.SecureJoin(srcRoot, src) + if err != nil { + return err + } + // If destination is a folder, we need to take extra care to + // ensure that files are copied with correct names (since + // resolving a symlink may result in a different name). + if hadFinalPathSeparator { + _, srcName := filepath.Split(src) + _, srcNameSecure := filepath.Split(srcSecure) + if srcName != srcNameSecure { + options := buildah.AddAndCopyOptions{ + Chown: copy.Chown, + ContextDir: contextDir, + Excludes: copyExcludes, + } + if err := s.builder.Add(filepath.Join(copy.Dest, srcName), copy.Download, options, srcSecure); err != nil { + return err + } + continue + } + } + sources = append(sources, srcSecure) + + } else { + sources = append(sources, filepath.Join(s.executor.contextDir, src)) + copyExcludes = append(s.executor.excludes, excludes...) + } + options := buildah.AddAndCopyOptions{ + Chown: copy.Chown, + ContextDir: contextDir, + Excludes: copyExcludes, + IDMappingOptions: idMappingOptions, + } + if err := s.builder.Add(copy.Dest, copy.Download, options, sources...); err != nil { + return err + } + } + } + return nil +} + +// Run executes a RUN instruction using the stage's current working container +// as a root directory. +func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error { + logrus.Debugf("RUN %#v, %#v", run, config) + if s.builder == nil { + return errors.Errorf("no build container available") + } + stdin := s.executor.in + if stdin == nil { + devNull, err := os.Open(os.DevNull) + if err != nil { + return errors.Errorf("error opening %q for reading: %v", os.DevNull, err) + } + defer devNull.Close() + stdin = devNull + } + options := buildah.RunOptions{ + Hostname: config.Hostname, + Runtime: s.executor.runtime, + Args: s.executor.runtimeArgs, + NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "", + Mounts: convertMounts(s.executor.transientMounts), + Env: config.Env, + User: config.User, + WorkingDir: config.WorkingDir, + Entrypoint: config.Entrypoint, + Cmd: config.Cmd, + Stdin: stdin, + Stdout: s.executor.out, + Stderr: s.executor.err, + Quiet: s.executor.quiet, + NamespaceOptions: s.executor.namespaceOptions, + } + if config.NetworkDisabled { + options.ConfigureNetwork = buildah.NetworkDisabled + } else { + options.ConfigureNetwork = buildah.NetworkEnabled + } + + args := run.Args + if run.Shell { + if len(config.Shell) > 0 && s.builder.Format == buildah.Dockerv2ImageManifest { + args = append(config.Shell, args...) + } else { + args = append([]string{"/bin/sh", "-c"}, args...) + } + } + if err := s.volumeCacheSave(); err != nil { + return err + } + err := s.builder.Run(args, options) + if err2 := s.volumeCacheRestore(); err2 != nil { + if err == nil { + return err2 + } + } + return err +} + +// UnrecognizedInstruction is called when we encounter an instruction that the +// imagebuilder parser didn't understand. +func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error { + errStr := fmt.Sprintf("Build error: Unknown instruction: %q ", strings.ToUpper(step.Command)) + err := fmt.Sprintf(errStr+"%#v", step) + if s.executor.ignoreUnrecognizedInstructions { + logrus.Debugf(err) + return nil + } + + switch logrus.GetLevel() { + case logrus.ErrorLevel: + logrus.Errorf(errStr) + case logrus.DebugLevel: + logrus.Debugf(err) + default: + logrus.Errorf("+(UNHANDLED LOGLEVEL) %#v", step) + } + + return errors.Errorf(err) +} + +// prepare creates a working container based on the specified image, or if one +// isn't specified, the first argument passed to the first FROM instruction we +// can find in the stage's parsed tree. +func (s *StageExecutor) prepare(ctx context.Context, stage imagebuilder.Stage, from string, initializeIBConfig, rebase bool) (builder *buildah.Builder, err error) { + ib := stage.Builder + node := stage.Node + + if from == "" { + base, err := ib.From(node) + if err != nil { + logrus.Debugf("prepare(node.Children=%#v)", node.Children) + return nil, errors.Wrapf(err, "error determining starting point for build") + } + from = base + } + displayFrom := from + + // stage.Name will be a numeric string for all stages without an "AS" clause + asImageName := stage.Name + if asImageName != "" { + if _, err := strconv.Atoi(asImageName); err != nil { + displayFrom = from + " AS " + asImageName + } + } + + if initializeIBConfig && rebase { + logrus.Debugf("FROM %#v", displayFrom) + if !s.executor.quiet { + s.executor.log("FROM %s", displayFrom) + } + } + + builderOptions := buildah.BuilderOptions{ + Args: ib.Args, + FromImage: from, + PullPolicy: s.executor.pullPolicy, + Registry: s.executor.registry, + BlobDirectory: s.executor.blobDirectory, + SignaturePolicyPath: s.executor.signaturePolicyPath, + ReportWriter: s.executor.reportWriter, + SystemContext: s.executor.systemContext, + Isolation: s.executor.isolation, + NamespaceOptions: s.executor.namespaceOptions, + ConfigureNetwork: s.executor.configureNetwork, + CNIPluginPath: s.executor.cniPluginPath, + CNIConfigDir: s.executor.cniConfigDir, + IDMappingOptions: s.executor.idmappingOptions, + CommonBuildOpts: s.executor.commonBuildOptions, + DefaultMountsFilePath: s.executor.defaultMountsFilePath, + Format: s.executor.outputFormat, + } + + // Check and see if the image is a pseudonym for the end result of a + // previous stage, named by an AS clause in the Dockerfile. + if asImageFound, ok := s.executor.imageMap[from]; ok { + builderOptions.FromImage = asImageFound + } + builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions) + if err != nil { + return nil, errors.Wrapf(err, "error creating build container") + } + + if initializeIBConfig { + volumes := map[string]struct{}{} + for _, v := range builder.Volumes() { + volumes[v] = struct{}{} + } + ports := map[docker.Port]struct{}{} + for _, p := range builder.Ports() { + ports[docker.Port(p)] = struct{}{} + } + dConfig := docker.Config{ + Hostname: builder.Hostname(), + Domainname: builder.Domainname(), + User: builder.User(), + Env: builder.Env(), + Cmd: builder.Cmd(), + Image: from, + Volumes: volumes, + WorkingDir: builder.WorkDir(), + Entrypoint: builder.Entrypoint(), + Labels: builder.Labels(), + Shell: builder.Shell(), + StopSignal: builder.StopSignal(), + OnBuild: builder.OnBuild(), + ExposedPorts: ports, + } + var rootfs *docker.RootFS + if builder.Docker.RootFS != nil { + rootfs = &docker.RootFS{ + Type: builder.Docker.RootFS.Type, + } + for _, id := range builder.Docker.RootFS.DiffIDs { + rootfs.Layers = append(rootfs.Layers, id.String()) + } + } + dImage := docker.Image{ + Parent: builder.FromImage, + ContainerConfig: dConfig, + Container: builder.Container, + Author: builder.Maintainer(), + Architecture: builder.Architecture(), + RootFS: rootfs, + } + dImage.Config = &dImage.ContainerConfig + err = ib.FromImage(&dImage, node) + if err != nil { + if err2 := builder.Delete(); err2 != nil { + logrus.Debugf("error deleting container which we failed to update: %v", err2) + } + return nil, errors.Wrapf(err, "error updating build context") + } + } + mountPoint, err := builder.Mount(builder.MountLabel) + if err != nil { + if err2 := builder.Delete(); err2 != nil { + logrus.Debugf("error deleting container which we failed to mount: %v", err2) + } + return nil, errors.Wrapf(err, "error mounting new container") + } + if rebase { + // Make this our "current" working container. + s.mountPoint = mountPoint + s.builder = builder + } + logrus.Debugln("Container ID:", builder.ContainerID) + return builder, nil +} + +// Delete deletes the stage's working container, if we have one. +func (s *StageExecutor) Delete() (err error) { + if s.builder != nil { + err = s.builder.Delete() + s.builder = nil + } + return err +} + +// stepRequiresLayer indicates whether or not the step should be followed by +// committing a layer container when creating an intermediate image. +func (*StageExecutor) stepRequiresLayer(step *imagebuilder.Step) bool { + switch strings.ToUpper(step.Command) { + case "ADD", "COPY", "RUN": + return true + } + return false +} + +// getImageRootfs checks for an image matching the passed-in name in local +// storage. If it isn't found, it pulls down a copy. Then, if we don't have a +// working container root filesystem based on the image, it creates one. Then +// it returns that root filesystem's location. +func (s *StageExecutor) getImageRootfs(ctx context.Context, stage imagebuilder.Stage, image string) (mountPoint string, err error) { + if builder, ok := s.executor.containerMap[image]; ok { + return builder.MountPoint, nil + } + builder, err := s.prepare(ctx, stage, image, false, false) + if err != nil { + return "", err + } + s.executor.containerMap[image] = builder + return builder.MountPoint, nil +} + +// Execute runs each of the steps in the stage's parsed tree, in turn. +func (s *StageExecutor) Execute(ctx context.Context, stage imagebuilder.Stage, base string) (imgID string, ref reference.Canonical, err error) { + ib := stage.Builder + checkForLayers := s.executor.layers && s.executor.useCache + moreStages := s.index < s.stages-1 + lastStage := !moreStages + imageIsUsedLater := moreStages && (s.executor.baseMap[stage.Name] || s.executor.baseMap[fmt.Sprintf("%d", stage.Position)]) + rootfsIsUsedLater := moreStages && (s.executor.rootfsMap[stage.Name] || s.executor.rootfsMap[fmt.Sprintf("%d", stage.Position)]) + + // If the base image's name corresponds to the result of an earlier + // stage, substitute that image's ID for the base image's name here. + // If not, then go on assuming that it's just a regular image that's + // either in local storage, or one that we have to pull from a + // registry. + if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage { + base = stageImage + } + + // Create the (first) working container for this stage. Reinitializing + // the imagebuilder configuration may alter the list of steps we have, + // so take a snapshot of them *after* that. + if _, err := s.prepare(ctx, stage, base, true, true); err != nil { + return "", nil, err + } + children := stage.Node.Children + + // A helper function to only log "COMMIT" as an explicit step if it's + // the very last step of a (possibly multi-stage) build. + logCommit := func(output string, instruction int) { + moreInstructions := instruction < len(children)-1 + if moreInstructions || moreStages { + return + } + commitMessage := "COMMIT" + if output != "" { + commitMessage = fmt.Sprintf("%s %s", commitMessage, output) + } + logrus.Debugf(commitMessage) + if !s.executor.quiet { + s.executor.log(commitMessage) + } + } + logImageID := func(imgID string) { + if s.executor.iidfile == "" { + fmt.Fprintf(s.executor.out, "%s\n", imgID) + } + } + + if len(children) == 0 { + // There are no steps. + if s.builder.FromImageID == "" || s.executor.squash { + // We either don't have a base image, or we need to + // squash the contents of the base image. Whichever is + // the case, we need to commit() to create a new image. + logCommit(s.output, -1) + if imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(nil), false, s.output); err != nil { + return "", nil, errors.Wrapf(err, "error committing base container") + } + } else { + // We don't need to squash the base image, so just + // reuse the base image. + logCommit(s.output, -1) + if imgID, ref, err = s.tagExistingImage(ctx, s.builder.FromImageID, s.output); err != nil { + return "", nil, err + } + } + logImageID(imgID) + } + + for i, node := range children { + moreInstructions := i < len(children)-1 + lastInstruction := !moreInstructions + // Resolve any arguments in this instruction. + step := ib.Step() + if err := step.Resolve(node); err != nil { + return "", nil, errors.Wrapf(err, "error resolving step %+v", *node) + } + logrus.Debugf("Parsed Step: %+v", *step) + if !s.executor.quiet { + s.executor.log("%s", step.Original) + } + + // Check if there's a --from if the step command is COPY or + // ADD. Set copyFrom to point to either the context directory + // or the root of the container from the specified stage. + s.copyFrom = s.executor.contextDir + for _, n := range step.Flags { + command := strings.ToUpper(step.Command) + if strings.Contains(n, "--from") && (command == "COPY" || command == "ADD") { + var mountPoint string + arr := strings.Split(n, "=") + otherStage, ok := s.executor.stages[arr[1]] + if !ok { + if mountPoint, err = s.getImageRootfs(ctx, stage, arr[1]); err != nil { + return "", nil, errors.Errorf("%s --from=%s: no stage or image found with that name", command, arr[1]) + } + } else { + mountPoint = otherStage.mountPoint + } + s.copyFrom = mountPoint + break + } + } + + // Determine if there are any RUN instructions to be run after + // this step. If not, we won't have to bother preserving the + // contents of any volumes declared between now and when we + // finish. + noRunsRemaining := false + if moreInstructions { + noRunsRemaining = !ib.RequiresStart(&parser.Node{Children: children[i+1:]}) + } + + // If we're doing a single-layer build, just process the + // instruction. + if !s.executor.layers { + err := ib.Run(step, s, noRunsRemaining) + if err != nil { + logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) + return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) + } + if moreInstructions { + // There are still more instructions to process + // for this stage. Make a note of the + // instruction in the history that we'll write + // for the image when we eventually commit it. + now := time.Now() + s.builder.AddPrependedEmptyLayer(&now, s.executor.getCreatedBy(node), "", "") + continue + } else { + // This is the last instruction for this stage, + // so we should commit this container to create + // an image, but only if it's the last one, or + // if it's used as the basis for a later stage. + if lastStage || imageIsUsedLater { + logCommit(s.output, i) + imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), false, s.output) + if err != nil { + return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step) + } + logImageID(imgID) + } else { + imgID = "" + } + break + } + } + + // We're in a multi-layered build. + var ( + commitName string + cacheID string + err error + rebase bool + ) + + // 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 we're using the cache, and we've managed to stick with + // cached images so far, look for one that matches what we + // expect to produce for this instruction. + if checkForLayers && !(s.executor.squash && lastInstruction && lastStage) { + cacheID, err = s.layerExists(ctx, node) + if err != nil { + return "", nil, errors.Wrap(err, "error checking if cached image exists from a previous build") + } + if cacheID != "" { + // Note the cache hit. + fmt.Fprintf(s.executor.out, "--> Using cache %s\n", cacheID) + } else { + // We're not going to find any more cache hits. + checkForLayers = false + } + } + + if cacheID != "" { + // A suitable cached image was found, so just reuse it. + // If we need to name the resulting image because it's + // the last step in this stage, add the name to the + // image. + imgID = cacheID + if commitName != "" { + logCommit(commitName, i) + if imgID, ref, err = s.tagExistingImage(ctx, cacheID, commitName); err != nil { + return "", nil, err + } + logImageID(imgID) + } + // Update our working container to be based off of the + // cached image, if we might need to use it as a basis + // for the next instruction, or if we need the root + // filesystem to match the image contents for the sake + // of a later stage that wants to copy content from it. + rebase = moreInstructions || rootfsIsUsedLater + // 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 + // last cache image will be all that we need, since we + // still don't want to restart using the image's + // configuration blob. + if !s.stepRequiresLayer(step) { + err := ib.Run(step, s, noRunsRemaining) + if err != nil { + logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) + return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) + } + } + } else { + // If we didn't find a cached image that we could just reuse, + // process the instruction directly. + err := ib.Run(step, s, noRunsRemaining) + if err != nil { + logrus.Debugf("%v", errors.Wrapf(err, "error building at step %+v", *step)) + return "", nil, errors.Wrapf(err, "error building at STEP \"%s\"", step.Message) + } + // Create a new image, maybe with a new layer. + logCommit(s.output, i) + imgID, ref, err = s.commit(ctx, ib, s.executor.getCreatedBy(node), !s.stepRequiresLayer(step), commitName) + if err != nil { + return "", nil, errors.Wrapf(err, "error committing container for step %+v", *step) + } + logImageID(imgID) + // We only need to build a new container rootfs + // using this image if we plan on making + // further changes to it. Subsequent stages + // that just want to use the rootfs as a source + // for COPY or ADD will be content with what we + // already have. + rebase = moreInstructions + } + + if rebase { + // Since we either committed the working container or + // are about to replace it with one based on a cached + // image, add the current working container's ID to the + // list of successful intermediate containers that + // we'll clean up later. + s.containerIDs = append(s.containerIDs, s.builder.ContainerID) + + // Prepare for the next step or subsequent phases by + // creating a new working container with the + // just-committed or updated cached image as its new + // base image. + if _, err := s.prepare(ctx, stage, imgID, false, true); err != nil { + return "", nil, errors.Wrap(err, "error preparing container for next step") + } + } + } + + return imgID, ref, nil +} + +// tagExistingImage adds names to an image already in the store +func (s *StageExecutor) tagExistingImage(ctx context.Context, cacheID, output string) (string, reference.Canonical, error) { + // If we don't need to attach a name to the image, just return the cache ID. + if output == "" { + return cacheID, nil, nil + } + + // Get the destination image reference. + dest, err := s.executor.resolveNameToImageRef(output) + if err != nil { + return "", nil, err + } + + policyContext, err := util.GetPolicyContext(s.executor.systemContext) + if err != nil { + return "", nil, err + } + defer func() { + if destroyErr := policyContext.Destroy(); destroyErr != nil { + if err == nil { + err = destroyErr + } else { + err = errors.Wrap(err, destroyErr.Error()) + } + } + }() + + // Look up the source image, expecting it to be in local storage + src, err := is.Transport.ParseStoreReference(s.executor.store, cacheID) + if err != nil { + return "", nil, errors.Wrapf(err, "error getting source imageReference for %q", cacheID) + } + manifestBytes, err := cp.Image(ctx, policyContext, dest, src, nil) + if err != nil { + return "", nil, errors.Wrapf(err, "error copying image %q", cacheID) + } + manifestDigest, err := manifest.Digest(manifestBytes) + if err != nil { + return "", nil, errors.Wrapf(err, "error computing digest of manifest for image %q", cacheID) + } + img, err := is.Transport.GetStoreImage(s.executor.store, dest) + if err != nil { + return "", nil, errors.Wrapf(err, "error locating new copy of image %q (i.e., %q)", cacheID, transports.ImageName(dest)) + } + var ref reference.Canonical + if dref := dest.DockerReference(); dref != nil { + if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { + return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q (i.e., %q)", cacheID, transports.ImageName(dest)) + } + } + return img.ID, ref, nil +} + +// layerExists 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) layerExists(ctx context.Context, currNode *parser.Node) (string, error) { + // Get the list of images available in the image store + images, err := s.executor.store.Images() + if err != nil { + return "", errors.Wrap(err, "error getting image list from store") + } + var baseHistory []v1.History + if s.builder.FromImageID != "" { + baseHistory, err = s.executor.getImageHistory(ctx, s.builder.FromImageID) + if err != nil { + return "", errors.Wrapf(err, "error getting history of base image %q", s.builder.FromImageID) + } + } + for _, image := range images { + var imageTopLayer *storage.Layer + if image.TopLayer != "" { + imageTopLayer, err = s.executor.store.Layer(image.TopLayer) + if err != nil { + return "", errors.Wrapf(err, "error getting top layer info") + } + } + // If the parent of the top layer of an image is equal to the current build image's top layer, + // it means that this image is potentially a cached intermediate image from a previous + // build. Next we double check that the history of this image is equivalent to the previous + // lines in the Dockerfile up till the point we are at in the build. + if imageTopLayer == nil || (s.builder.TopLayer != "" && (imageTopLayer.Parent == s.builder.TopLayer || imageTopLayer.ID == s.builder.TopLayer)) { + history, err := s.executor.getImageHistory(ctx, image.ID) + if err != nil { + return "", errors.Wrapf(err, "error getting history of %q", image.ID) + } + // children + currNode is the point of the Dockerfile we are currently at. + if s.executor.historyMatches(baseHistory, currNode, history) { + // This checks if the files copied during build have been changed if the node is + // a COPY or ADD command. + filesMatch, err := s.copiedFilesMatch(currNode, history[len(history)-1].Created) + if err != nil { + return "", errors.Wrapf(err, "error checking if copied files match") + } + if filesMatch { + return image.ID, nil + } + } + } + } + return "", nil +} + +// getFilesToCopy goes through node to get all the src files that are copied, added or downloaded. +// It is possible for the Dockerfile to have src as hom*, which means all files that have hom as a prefix. +// Another format is hom?.txt, which means all files that have that name format with the ? replaced by another character. +func (s *StageExecutor) getFilesToCopy(node *parser.Node) ([]string, error) { + currNode := node.Next + var src []string + for currNode.Next != nil { + if strings.HasPrefix(currNode.Value, "http://") || strings.HasPrefix(currNode.Value, "https://") { + src = append(src, currNode.Value) + currNode = currNode.Next + continue + } + matches, err := filepath.Glob(filepath.Join(s.copyFrom, currNode.Value)) + if err != nil { + return nil, errors.Wrapf(err, "error finding match for pattern %q", currNode.Value) + } + src = append(src, matches...) + currNode = currNode.Next + } + return src, nil +} + +// copiedFilesMatch checks to see if the node instruction is a COPY or ADD. +// If it is either of those two it checks the timestamps on all the files copied/added +// by the dockerfile. If the host version has a time stamp greater than the time stamp +// of the build, the build will not use the cached version and will rebuild. +func (s *StageExecutor) copiedFilesMatch(node *parser.Node, historyTime *time.Time) (bool, error) { + if node.Value != "add" && node.Value != "copy" { + return true, nil + } + + src, err := s.getFilesToCopy(node) + if err != nil { + return false, err + } + for _, item := range src { + // for urls, check the Last-Modified field in the header. + if strings.HasPrefix(item, "http://") || strings.HasPrefix(item, "https://") { + urlContentNew, err := urlContentModified(item, historyTime) + if err != nil { + return false, err + } + if urlContentNew { + return false, nil + } + continue + } + // Walks the file tree for local files and uses chroot to ensure we don't escape out of the allowed path + // when resolving any symlinks. + // Change the time format to ensure we don't run into a parsing error when converting again from string + // to time.Time. It is a known Go issue that the conversions cause errors sometimes, so specifying a particular + // time format here when converting to a string. + timeIsGreater, err := resolveModifiedTime(s.copyFrom, item, historyTime.Format(time.RFC3339Nano)) + if err != nil { + return false, errors.Wrapf(err, "error resolving symlinks and comparing modified times: %q", item) + } + if timeIsGreater { + return false, nil + } + } + return true, nil +} + +// commit writes the container's contents to an image, using a passed-in tag as +// the name if there is one, generating a unique ID-based one otherwise. +func (s *StageExecutor) commit(ctx context.Context, ib *imagebuilder.Builder, createdBy string, emptyLayer bool, output string) (string, reference.Canonical, error) { + var imageRef types.ImageReference + if output != "" { + imageRef2, err := s.executor.resolveNameToImageRef(output) + if err != nil { + return "", nil, err + } + imageRef = imageRef2 + } + + if ib.Author != "" { + s.builder.SetMaintainer(ib.Author) + } + config := ib.Config() + if createdBy != "" { + s.builder.SetCreatedBy(createdBy) + } + s.builder.SetHostname(config.Hostname) + s.builder.SetDomainname(config.Domainname) + s.builder.SetUser(config.User) + s.builder.ClearPorts() + for p := range config.ExposedPorts { + s.builder.SetPort(string(p)) + } + for _, envSpec := range config.Env { + spec := strings.SplitN(envSpec, "=", 2) + s.builder.SetEnv(spec[0], spec[1]) + } + s.builder.SetCmd(config.Cmd) + s.builder.ClearVolumes() + for v := range config.Volumes { + s.builder.AddVolume(v) + } + s.builder.ClearOnBuild() + for _, onBuildSpec := range config.OnBuild { + s.builder.SetOnBuild(onBuildSpec) + } + s.builder.SetWorkDir(config.WorkingDir) + s.builder.SetEntrypoint(config.Entrypoint) + s.builder.SetShell(config.Shell) + s.builder.SetStopSignal(config.StopSignal) + if config.Healthcheck != nil { + s.builder.SetHealthcheck(&buildahdocker.HealthConfig{ + Test: append([]string{}, config.Healthcheck.Test...), + Interval: config.Healthcheck.Interval, + Timeout: config.Healthcheck.Timeout, + StartPeriod: config.Healthcheck.StartPeriod, + Retries: config.Healthcheck.Retries, + }) + } else { + s.builder.SetHealthcheck(nil) + } + s.builder.ClearLabels() + for k, v := range config.Labels { + s.builder.SetLabel(k, v) + } + for _, labelSpec := range s.executor.labels { + label := strings.SplitN(labelSpec, "=", 2) + if len(label) > 1 { + s.builder.SetLabel(label[0], label[1]) + } else { + s.builder.SetLabel(label[0], "") + } + } + for _, annotationSpec := range s.executor.annotations { + annotation := strings.SplitN(annotationSpec, "=", 2) + if len(annotation) > 1 { + s.builder.SetAnnotation(annotation[0], annotation[1]) + } else { + s.builder.SetAnnotation(annotation[0], "") + } + } + if imageRef != nil { + logName := transports.ImageName(imageRef) + logrus.Debugf("COMMIT %q", logName) + } else { + logrus.Debugf("COMMIT") + } + writer := s.executor.reportWriter + if s.executor.layers || !s.executor.useCache { + writer = nil + } + options := buildah.CommitOptions{ + Compression: s.executor.compression, + SignaturePolicyPath: s.executor.signaturePolicyPath, + ReportWriter: writer, + PreferredManifestType: s.executor.outputFormat, + SystemContext: s.executor.systemContext, + Squash: s.executor.squash, + EmptyLayer: emptyLayer, + BlobDirectory: s.executor.blobDirectory, + } + imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options) + if err != nil { + return "", nil, err + } + var ref reference.Canonical + if imageRef != nil { + if dref := imageRef.DockerReference(); dref != nil { + if ref, err = reference.WithDigest(dref, manifestDigest); err != nil { + return "", nil, errors.Wrapf(err, "error computing canonical reference for new image %q", imgID) + } + } + } + return imgID, ref, nil +} + +func (s *StageExecutor) EnsureContainerPath(path string) error { + targetPath, err := securejoin.SecureJoin(s.mountPoint, path) + if err != nil { + return errors.Wrapf(err, "error ensuring container path %q", path) + } + _, err = os.Lstat(targetPath) + if err != nil && os.IsNotExist(err) { + err = os.MkdirAll(targetPath, 0755) + } + if err != nil { + return errors.Wrapf(err, "error ensuring container path %q", path) + } + return nil +} + +// urlContentModified sends a get request to the url and checks if the header has a value in +// Last-Modified, and if it does compares the time stamp to that of the history of the cached image. +// returns true if there is no Last-Modified value in the header. +func urlContentModified(url string, historyTime *time.Time) (bool, error) { + resp, err := http.Get(url) + if err != nil { + return false, errors.Wrapf(err, "error getting %q", url) + } + defer resp.Body.Close() + if lastModified := resp.Header.Get("Last-Modified"); lastModified != "" { + lastModifiedTime, err := time.Parse(time.RFC1123, lastModified) + if err != nil { + return false, errors.Wrapf(err, "error parsing time for %q", url) + } + return lastModifiedTime.After(*historyTime), nil + } + logrus.Debugf("Response header did not have Last-Modified %q, will rebuild.", url) + return true, nil +} diff --git a/vendor/github.com/containers/buildah/imagebuildah/util.go b/vendor/github.com/containers/buildah/imagebuildah/util.go index 3962d1a9d..4b020bf41 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/util.go +++ b/vendor/github.com/containers/buildah/imagebuildah/util.go @@ -12,6 +12,7 @@ import ( "github.com/containers/buildah" "github.com/containers/storage/pkg/chrootarchive" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -105,21 +106,23 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err return "", "", errors.Errorf("unreachable code reached") } -func dedupeStringSlice(slice []string) []string { - done := make([]string, 0, len(slice)) - m := make(map[string]struct{}) - for _, s := range slice { - if _, present := m[s]; !present { - m[s] = struct{}{} - done = append(done, s) - } - } - return done -} - // InitReexec is a wrapper for buildah.InitReexec(). It should be called at // the start of main(), and if it returns true, main() should return // immediately. func InitReexec() bool { return buildah.InitReexec() } + +func convertMounts(mounts []Mount) []specs.Mount { + specmounts := []specs.Mount{} + for _, m := range mounts { + s := specs.Mount{ + Destination: m.Destination, + Type: m.Type, + Source: m.Source, + Options: m.Options, + } + specmounts = append(specmounts, s) + } + return specmounts +} diff --git a/vendor/github.com/containers/buildah/import.go b/vendor/github.com/containers/buildah/import.go index 418487438..b01d4d07b 100644 --- a/vendor/github.com/containers/buildah/import.go +++ b/vendor/github.com/containers/buildah/import.go @@ -5,6 +5,7 @@ import ( "github.com/containers/buildah/docker" "github.com/containers/buildah/util" + "github.com/containers/image/manifest" is "github.com/containers/image/storage" "github.com/containers/image/types" "github.com/containers/storage" @@ -47,6 +48,13 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system } } + imageDigest := "" + if manifestBytes, _, err := src.Manifest(ctx); err == nil { + if manifestDigest, err := manifest.Digest(manifestBytes); err == nil { + imageDigest = manifestDigest.String() + } + } + defaultNamespaceOptions, err := DefaultNamespaceOptions() if err != nil { return nil, err @@ -57,6 +65,7 @@ func importBuilderDataFromImage(ctx context.Context, store storage.Store, system Type: containerType, FromImage: imageName, FromImageID: imageID, + FromImageDigest: imageDigest, Container: containerName, ContainerID: containerID, ImageAnnotations: map[string]string{}, diff --git a/vendor/github.com/containers/buildah/info.go b/vendor/github.com/containers/buildah/info.go index 32b54f257..ed21ac1e9 100644 --- a/vendor/github.com/containers/buildah/info.go +++ b/vendor/github.com/containers/buildah/info.go @@ -27,10 +27,7 @@ type InfoData struct { func Info(store storage.Store) ([]InfoData, error) { info := []InfoData{} // get host information - hostInfo, err := hostInfo() - if err != nil { - logrus.Error(err, "error getting host info") - } + hostInfo := hostInfo() info = append(info, InfoData{Type: "host", Data: hostInfo}) // get store information @@ -42,7 +39,7 @@ func Info(store storage.Store) ([]InfoData, error) { return info, nil } -func hostInfo() (map[string]interface{}, error) { +func hostInfo() map[string]interface{} { info := map[string]interface{}{} info["os"] = runtime.GOOS info["arch"] = runtime.GOARCH @@ -115,8 +112,7 @@ func hostInfo() (map[string]interface{}, error) { } info["hostname"] = host - return info, nil - + return info } // top-level "store" info diff --git a/vendor/github.com/containers/buildah/install.md b/vendor/github.com/containers/buildah/install.md index 931a45e88..6ff2f327a 100644 --- a/vendor/github.com/containers/buildah/install.md +++ b/vendor/github.com/containers/buildah/install.md @@ -274,16 +274,7 @@ The build steps on Debian are otherwise the same as Ubuntu, above. ## Vendoring - Dependency Management -This project is using [vndr](https://github.com/LK4D4/vndr) for managing dependencies, which is a tedious and error-prone task. Doing it manually is likely to cause inconsistencies between the `./vendor` directory (i.e., the downloaded dependencies), the source code that imports those dependencies and the `vendor.conf` configuration file that describes which packages in which version (e.g., a release or git commit) are a dependency. - -To ease updating dependencies, we provide the `make vendor` target, which fetches all dependencies mentioned in `vendor.conf`. `make vendor` whitelists certain packages to prevent the `vndr` tool from removing packages that the test suite (see `./test`) imports. - -The CI of this project makes sure that each pull request leaves a clean vendor state behind by first running the aforementioned `make vendor` followed by running `./hack/tree_status.sh` which checks if any file in the git tree has changed. - -### Vendor Troubleshooting - -If the CI is complaining about a pull request leaving behind an unclean state, it is very likely right about it. Make sure to run `make vendor` and add all the changes to the commit. Also make sure that your local git tree does not include files not under version control that may reference other go packages. If some dependencies are removed but they should not, for instance, because the CI is needing them, then whitelist those dependencies in the `make vendor` target of the Makefile. Whitelisting a package will instruct `vndr` to not remove if during its cleanup phase. -sd +This project is using [go modules](https://github.com/golang/go/wiki/Modules) for dependency management. If the CI is complaining about a pull request leaving behind an unclean state, it is very likely right about it. After changing dependencies, make sure to run `make vendor` to synchronize the code with the go module and repopulate the `./vendor` directory. ## Configuration files @@ -383,3 +374,14 @@ cat /etc/containers/policy.json } } ``` + +## Vendoring + +Buildah uses Go Modules for vendoring purposes. If you need to update or add a vendored package into Buildah, please follow this proceedure: + * Enter into your sandbox `src/github.com/containers/buildah` and ensure that he GOPATH variable is set to the directory prior as noted above. + * `export GO111MODULE=on` + * Assuming you want to 'bump' the `github.com/containers/storage` package to version 1.12.13, use this command: `go get github.com/containers/storage@v1.12.13` + * `make vendor` + * `make` + * `make install` + * Then add any updated or added files with `git add` then do a `git commit` and create a PR. diff --git a/vendor/github.com/containers/buildah/new.go b/vendor/github.com/containers/buildah/new.go index ecd1666bf..72d53dd99 100644 --- a/vendor/github.com/containers/buildah/new.go +++ b/vendor/github.com/containers/buildah/new.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/containers/buildah/util" + "github.com/containers/image/manifest" "github.com/containers/image/pkg/sysregistries" is "github.com/containers/image/storage" "github.com/containers/image/transports" @@ -254,6 +255,7 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions } image := options.FromImage imageID := "" + imageDigest := "" topLayer := "" if img != nil { image = getImageName(imageNamePrefix(image), img) @@ -266,6 +268,11 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions if err != nil { return nil, errors.Wrapf(err, "error instantiating image for %q", transports.ImageName(ref)) } + if manifestBytes, _, err := src.Manifest(ctx); err == nil { + if manifestDigest, err := manifest.Digest(manifestBytes); err == nil { + imageDigest = manifestDigest.String() + } + } defer src.Close() } @@ -327,6 +334,7 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions Type: containerType, FromImage: image, FromImageID: imageID, + FromImageDigest: imageDigest, Container: name, ContainerID: container.ID, ImageAnnotations: map[string]string{}, diff --git a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go index 31e6a428c..f2f2cac79 100644 --- a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go +++ b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go @@ -18,7 +18,7 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/ioutils" digest "github.com/opencontainers/go-digest" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -83,7 +83,7 @@ func makeFilename(blobSum digest.Digest, isConfig bool) string { // NewBlobCache creates a new blob cache that wraps an image reference. Any blobs which are // written to the destination image created from the resulting reference will also be stored -// as-is to the specifed directory or a temporary directory. The cache directory's contents +// as-is to the specified directory or a temporary directory. The cache directory's contents // can be cleared by calling the returned BlobCache()'s ClearCache() method. // The compress argument controls whether or not the cache will try to substitute a compressed // or different version of a blob when preparing the list of layers when reading an image. @@ -379,7 +379,9 @@ func saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os _, err3 = io.Copy(io.MultiWriter(tempFile, digester.Hash()), decompressed) } else { // Drain the pipe to keep from stalling the PutBlob() thread. - io.Copy(ioutil.Discard, decompressReader) + if _, err := io.Copy(ioutil.Discard, decompressReader); err != nil { + logrus.Debugf("error draining the pipe: %v", err) + } } decompressReader.Close() decompressed.Close() diff --git a/vendor/github.com/containers/buildah/pkg/cli/common.go b/vendor/github.com/containers/buildah/pkg/cli/common.go index 23bb696fc..81f3e8f32 100644 --- a/vendor/github.com/containers/buildah/pkg/cli/common.go +++ b/vendor/github.com/containers/buildah/pkg/cli/common.go @@ -118,7 +118,9 @@ func GetNameSpaceFlags(flags *NameSpaceResults) pflag.FlagSet { fs.StringVar(&flags.Network, string(specs.NetworkNamespace), "", "'container', `path` of network namespace to join, or 'host'") // TODO How do we alias net and network? fs.StringVar(&flags.Network, "net", "", "'container', `path` of network namespace to join, or 'host'") - fs.MarkHidden("net") + if err := fs.MarkHidden("net"); err != nil { + panic(fmt.Sprintf("error marking net flag as hidden: %v", err)) + } fs.StringVar(&flags.CNIConfigDir, "cni-config-dir", util.DefaultCNIConfigDir, "`directory` of CNI configuration files") fs.StringVar(&flags.CNIPlugInPath, "cni-plugin-path", util.DefaultCNIPluginPath, "`path` of CNI network plugins") fs.StringVar(&flags.PID, string(specs.PIDNamespace), "", "container, `path` of PID namespace to join, or 'host'") @@ -172,7 +174,9 @@ func GetFromAndBudFlags(flags *FromAndBudResults, usernsResults *UserNSResults, fs := pflag.FlagSet{} fs.StringSliceVar(&flags.AddHost, "add-host", []string{}, "add a custom host-to-IP mapping (`host:ip`) (default [])") fs.StringVar(&flags.BlobCache, "blob-cache", "", "assume image blobs in the specified directory will be available for pushing") - fs.MarkHidden("blob-cache") + if err := fs.MarkHidden("blob-cache"); err != nil { + panic(fmt.Sprintf("error marking net flag as hidden: %v", err)) + } fs.StringSliceVar(&flags.CapAdd, "cap-add", []string{}, "add the specified capability when running (default [])") fs.StringSliceVar(&flags.CapDrop, "cap-drop", []string{}, "drop the specified capability when running (default [])") fs.StringVar(&flags.CgroupParent, "cgroup-parent", "", "optional parent cgroup for the container") diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse.go b/vendor/github.com/containers/buildah/pkg/parse/parse.go index 61e70cdd3..78cbee746 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse.go @@ -30,6 +30,17 @@ const ( SeccompDefaultPath = "/usr/share/containers/seccomp.json" // SeccompOverridePath if this exists it overrides the default seccomp path SeccompOverridePath = "/etc/crio/seccomp.json" + // TypeBind is the type for mounting host dir + TypeBind = "bind" + // TypeTmpfs is the type for mounting tmpfs + TypeTmpfs = "tmpfs" +) + +var ( + errBadMntOption = errors.Errorf("invalid mount option") + errDuplicateDest = errors.Errorf("duplicate mount destination") + optionArgError = errors.Errorf("must provide an argument for option") + noDestError = errors.Errorf("must set volume destination") ) // CommonBuildOptions parses the build options from the bud cli @@ -168,13 +179,14 @@ func parseSecurityOpts(securityOpts []string, commonOpts *buildah.CommonBuildOpt return nil } +// ParseVolume parses the input of --volume func ParseVolume(volume string) (specs.Mount, error) { mount := specs.Mount{} arr := strings.SplitN(volume, ":", 3) if len(arr) < 2 { return mount, errors.Errorf("incorrect volume format %q, should be host-dir:ctr-dir[:option]", volume) } - if err := ValidateVolumeHostDir(arr[0]); err != nil { + if err := validateVolumeMountHostDir(arr[0]); err != nil { return mount, err } if err := ValidateVolumeCtrDir(arr[1]); err != nil { @@ -183,7 +195,7 @@ func ParseVolume(volume string) (specs.Mount, error) { mountOptions := "" if len(arr) > 2 { mountOptions = arr[2] - if err := ValidateVolumeOpts(arr[2]); err != nil { + if _, err := ValidateVolumeOpts(strings.Split(arr[2], ",")); err != nil { return mount, err } } @@ -208,7 +220,227 @@ func ParseVolumes(volumes []string) error { return nil } +func getVolumeMounts(volumes []string) (map[string]specs.Mount, error) { + finalVolumeMounts := make(map[string]specs.Mount) + + for _, volume := range volumes { + volumeMount, err := ParseVolume(volume) + if err != nil { + return nil, err + } + if _, ok := finalVolumeMounts[volumeMount.Destination]; ok { + return nil, errors.Wrapf(errDuplicateDest, volumeMount.Destination) + } + finalVolumeMounts[volumeMount.Destination] = volumeMount + } + return finalVolumeMounts, nil +} + +// GetVolumes gets the volumes from --volume and --mount +func GetVolumes(volumes []string, mounts []string) ([]specs.Mount, error) { + unifiedMounts, err := getMounts(mounts) + if err != nil { + return nil, err + } + volumeMounts, err := getVolumeMounts(volumes) + if err != nil { + return nil, err + } + for dest, mount := range volumeMounts { + if _, ok := unifiedMounts[dest]; ok { + return nil, errors.Wrapf(errDuplicateDest, dest) + } + unifiedMounts[dest] = mount + } + + finalMounts := make([]specs.Mount, 0, len(unifiedMounts)) + for _, mount := range unifiedMounts { + finalMounts = append(finalMounts, mount) + } + return finalMounts, nil +} + +// getMounts takes user-provided input from the --mount flag and creates OCI +// spec mounts. +// buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... +// buildah run --mount type=tmpfs,target=/dev/shm ... +func getMounts(mounts []string) (map[string]specs.Mount, error) { + finalMounts := make(map[string]specs.Mount) + + errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>[,options]") + + // TODO(vrothberg): the manual parsing can be replaced with a regular expression + // to allow a more robust parsing of the mount format and to give + // precise errors regarding supported format versus suppored options. + for _, mount := range mounts { + arr := strings.SplitN(mount, ",", 2) + if len(arr) < 2 { + return nil, errors.Wrapf(errInvalidSyntax, "%q", mount) + } + kv := strings.Split(arr[0], "=") + // TODO: type is not explicitly required in Docker. + // If not specified, it defaults to "volume". + if len(kv) != 2 || kv[0] != "type" { + return nil, errors.Wrapf(errInvalidSyntax, "%q", mount) + } + + tokens := strings.Split(arr[1], ",") + switch kv[1] { + case TypeBind: + mount, err := GetBindMount(tokens) + if err != nil { + return nil, err + } + if _, ok := finalMounts[mount.Destination]; ok { + return nil, errors.Wrapf(errDuplicateDest, mount.Destination) + } + finalMounts[mount.Destination] = mount + case TypeTmpfs: + mount, err := GetTmpfsMount(tokens) + if err != nil { + return nil, err + } + if _, ok := finalMounts[mount.Destination]; ok { + return nil, errors.Wrapf(errDuplicateDest, mount.Destination) + } + finalMounts[mount.Destination] = mount + default: + return nil, errors.Errorf("invalid filesystem type %q", kv[1]) + } + } + + return finalMounts, nil +} + +// GetBindMount parses a single bind mount entry from the --mount flag. +func GetBindMount(args []string) (specs.Mount, error) { + newMount := specs.Mount{ + Type: TypeBind, + } + + setSource := false + setDest := false + + for _, val := range args { + kv := strings.Split(val, "=") + switch kv[0] { + case "bind-nonrecursive": + newMount.Options = append(newMount.Options, "bind") + case "ro", "nosuid", "nodev", "noexec": + // TODO: detect duplication of these options. + // (Is this necessary?) + newMount.Options = append(newMount.Options, kv[0]) + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z": + newMount.Options = append(newMount.Options, kv[0]) + case "bind-propagation": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, kv[1]) + case "src", "source": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := ValidateVolumeHostDir(kv[1]); err != nil { + return newMount, err + } + newMount.Source = kv[1] + setSource = true + case "target", "dst", "destination": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := ValidateVolumeCtrDir(kv[1]); err != nil { + return newMount, err + } + newMount.Destination = kv[1] + setDest = true + default: + return newMount, errors.Wrapf(errBadMntOption, kv[0]) + } + } + + if !setDest { + return newMount, noDestError + } + + if !setSource { + newMount.Source = newMount.Destination + } + + opts, err := ValidateVolumeOpts(newMount.Options) + if err != nil { + return newMount, err + } + newMount.Options = opts + + return newMount, nil +} + +// GetTmpfsMount parses a single tmpfs mount entry from the --mount flag +func GetTmpfsMount(args []string) (specs.Mount, error) { + newMount := specs.Mount{ + Type: TypeTmpfs, + Source: TypeTmpfs, + } + + setDest := false + + for _, val := range args { + kv := strings.Split(val, "=") + switch kv[0] { + case "ro", "nosuid", "nodev", "noexec": + newMount.Options = append(newMount.Options, kv[0]) + case "tmpfs-mode": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) + case "tmpfs-size": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) + case "src", "source": + return newMount, errors.Errorf("source is not supported with tmpfs mounts") + case "target", "dst", "destination": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := ValidateVolumeCtrDir(kv[1]); err != nil { + return newMount, err + } + newMount.Destination = kv[1] + setDest = true + default: + return newMount, errors.Wrapf(errBadMntOption, kv[0]) + } + } + + if !setDest { + return newMount, noDestError + } + + return newMount, nil +} + +// ValidateVolumeHostDir validates a volume mount's source directory func ValidateVolumeHostDir(hostDir string) error { + if len(hostDir) == 0 { + return errors.Errorf("host directory cannot be empty") + } + if filepath.IsAbs(hostDir) { + if _, err := os.Stat(hostDir); err != nil { + return errors.Wrapf(err, "error checking path %q", hostDir) + } + } + // If hostDir is not an absolute path, that means the user wants to create a + // named volume. This will be done later on in the code. + return nil +} + +// validates the host path of buildah --volume +func validateVolumeMountHostDir(hostDir string) error { if !filepath.IsAbs(hostDir) { return errors.Errorf("invalid host path, must be an absolute path %q", hostDir) } @@ -218,41 +450,60 @@ func ValidateVolumeHostDir(hostDir string) error { return nil } +// ValidateVolumeCtrDir validates a volume mount's destination directory. func ValidateVolumeCtrDir(ctrDir string) error { + if len(ctrDir) == 0 { + return errors.Errorf("container directory cannot be empty") + } if !filepath.IsAbs(ctrDir) { - return errors.Errorf("invalid container path, must be an absolute path %q", ctrDir) + return errors.Errorf("invalid container path %q, must be an absolute path", ctrDir) } return nil } -func ValidateVolumeOpts(option string) error { - var foundRootPropagation, foundRWRO, foundLabelChange int - options := strings.Split(option, ",") +// ValidateVolumeOpts validates a volume's options +func ValidateVolumeOpts(options []string) ([]string, error) { + var foundRootPropagation, foundRWRO, foundLabelChange, bindType int + finalOpts := make([]string, 0, len(options)) for _, opt := range options { switch opt { case "rw", "ro": if foundRWRO > 1 { - return errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", option) + return nil, errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", ")) } foundRWRO++ case "z", "Z", "O": if opt == "O" && unshare.IsRootless() { - return errors.Errorf("invalid options %q, overlay mounts not supported in rootless mode", option) + return nil, errors.Errorf("invalid options %q, overlay mounts not supported in rootless mode", strings.Join(options, ", ")) } if foundLabelChange > 1 { - return errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", option) + return nil, errors.Errorf("invalid options %q, can only specify 1 'z', 'Z', or 'O' option", strings.Join(options, ", ")) } foundLabelChange++ case "private", "rprivate", "shared", "rshared", "slave", "rslave", "unbindable", "runbindable": if foundRootPropagation > 1 { - return errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private', '[r]slave' or '[r]unbindable' option", option) + return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private', '[r]slave' or '[r]unbindable' option", strings.Join(options, ", ")) } foundRootPropagation++ + case "bind", "rbind": + bindType++ + if bindType > 1 { + return nil, errors.Errorf("invalid options %q, can only specify 1 '[r]bind' option", strings.Join(options, ", ")) + } + case "cached", "delegated": + // The discarded ops are OS X specific volume options + // introduced in a recent Docker version. + // They have no meaning on Linux, so here we silently + // drop them. This matches Docker's behavior (the options + // are intended to be always safe to use, even not on OS + // X). + continue default: - return errors.Errorf("invalid option type %q", option) + return nil, errors.Errorf("invalid option type %q", opt) } + finalOpts = append(finalOpts, opt) } - return nil + return finalOpts, nil } // validateExtraHost validates that the specified string is a valid extrahost and returns it. @@ -538,7 +789,7 @@ func NamespaceOptions(c *cobra.Command) (namespaceOptions buildah.NamespaceOptio Host: true, }) default: - if what == specs.NetworkNamespace { + if what == string(specs.NetworkNamespace) { if how == "none" { options.AddOrReplace(buildah.NamespaceOption{ Name: what, diff --git a/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go b/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go index d056c1bb3..0574fc24e 100644 --- a/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go +++ b/vendor/github.com/containers/buildah/pkg/parse/parse_unix.go @@ -4,6 +4,7 @@ package parse import ( "fmt" + "golang.org/x/sys/unix" ) diff --git a/vendor/github.com/containers/buildah/pkg/unshare/unshare.go b/vendor/github.com/containers/buildah/pkg/unshare/unshare.go index 21b102cf5..ed83908c2 100644 --- a/vendor/github.com/containers/buildah/pkg/unshare/unshare.go +++ b/vendor/github.com/containers/buildah/pkg/unshare/unshare.go @@ -128,7 +128,9 @@ func (c *Cmd) Start() error { // Read the child's PID from the pipe. pidString := "" b := new(bytes.Buffer) - io.Copy(b, pidRead) + if _, err := io.Copy(b, pidRead); err != nil { + return errors.Wrapf(err, "error reading child PID") + } pidString = b.String() pid, err := strconv.Atoi(pidString) if err != nil { @@ -457,32 +459,38 @@ func MaybeReexecUsingUserNamespace(evenForRoot bool) { // Finish up. logrus.Debugf("running %+v with environment %+v, UID map %+v, and GID map %+v", cmd.Cmd.Args, os.Environ(), cmd.UidMappings, cmd.GidMappings) - ExecRunnable(cmd) + ExecRunnable(cmd, nil) } // ExecRunnable runs the specified unshare command, captures its exit status, // and exits with the same status. -func ExecRunnable(cmd Runnable) { +func ExecRunnable(cmd Runnable, cleanup func()) { + exit := func(status int) { + if cleanup != nil { + cleanup() + } + os.Exit(status) + } if err := cmd.Run(); err != nil { if exitError, ok := errors.Cause(err).(*exec.ExitError); ok { if exitError.ProcessState.Exited() { if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok { if waitStatus.Exited() { logrus.Errorf("%v", exitError) - os.Exit(waitStatus.ExitStatus()) + exit(waitStatus.ExitStatus()) } if waitStatus.Signaled() { logrus.Errorf("%v", exitError) - os.Exit(int(waitStatus.Signal()) + 128) + exit(int(waitStatus.Signal()) + 128) } } } } logrus.Errorf("%v", err) logrus.Errorf("(unable to determine exit status)") - os.Exit(1) + exit(1) } - os.Exit(0) + exit(0) } // getHostIDMappings reads mappings from the named node under /proc. diff --git a/vendor/github.com/containers/buildah/pull.go b/vendor/github.com/containers/buildah/pull.go index 66e573fa7..4e34f3fad 100644 --- a/vendor/github.com/containers/buildah/pull.go +++ b/vendor/github.com/containers/buildah/pull.go @@ -195,7 +195,9 @@ func Pull(ctx context.Context, imageName string, options PullOptions) (imageID s return "", errors.Wrapf(err, "internal error creating docker.Transport reference for %s", tagged.String()) } if options.ReportWriter != nil { - options.ReportWriter.Write([]byte("Pulling " + tagged.String() + "\n")) + if _, err := options.ReportWriter.Write([]byte("Pulling " + tagged.String() + "\n")); err != nil { + return "", errors.Wrapf(err, "error writing pull report") + } } ref, err := pullImage(ctx, options.Store, taggedRef, options, systemContext) if err != nil { @@ -263,7 +265,7 @@ func pullImage(ctx context.Context, store storage.Store, srcRef types.ImageRefer }() logrus.Debugf("copying %q to %q", transports.ImageName(srcRef), destName) - if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(store, options.ReportWriter, srcRef, sc, maybeCachedDestRef, nil, "")); err != nil { + if _, err := cp.Image(ctx, policyContext, maybeCachedDestRef, srcRef, getCopyOptions(store, options.ReportWriter, sc, nil, "")); err != nil { logrus.Debugf("error copying src image [%q] to dest image [%q] err: %v", transports.ImageName(srcRef), destName, err) return nil, err } diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go index 0bf37da59..9a2a396cb 100644 --- a/vendor/github.com/containers/buildah/run_linux.go +++ b/vendor/github.com/containers/buildah/run_linux.go @@ -175,7 +175,7 @@ func (b *Builder) Run(command []string, options RunOptions) error { } if !(contains(volumes, "/etc/resolv.conf") || (len(b.CommonBuildOpts.DNSServers) == 1 && strings.ToLower(b.CommonBuildOpts.DNSServers[0]) == "none")) { - resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair, b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions) + resolvFile, err := b.addNetworkConfig(path, "/etc/resolv.conf", rootIDPair, b.CommonBuildOpts.DNSServers, b.CommonBuildOpts.DNSSearch, b.CommonBuildOpts.DNSOptions, namespaceOptions) if err != nil { return err } @@ -217,7 +217,7 @@ func (b *Builder) Run(command []string, options RunOptions) error { if options.NoPivot { moreCreateArgs = append(moreCreateArgs, "--no-pivot") } - if err := setupRootlessSpecChanges(spec, path, rootUID, rootGID, b.CommonBuildOpts.ShmSize); err != nil { + if err := setupRootlessSpecChanges(spec, path, b.CommonBuildOpts.ShmSize); err != nil { return err } err = b.runUsingRuntimeSubproc(isolation, options, configureNetwork, configureNetworks, moreCreateArgs, spec, mountPoint, path, Package+"-"+filepath.Base(path)) @@ -411,10 +411,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st } // Get the list of files we need to bind into the container. - bindFileMounts, err := runSetupBoundFiles(bundlePath, bindFiles) - if err != nil { - return err - } + bindFileMounts := runSetupBoundFiles(bundlePath, bindFiles) // After this point we need to know the per-container persistent storage directory. cdir, err := b.store.ContainerDirectory(b.ContainerID) @@ -462,7 +459,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st } // addNetworkConfig copies files from host and sets them up to bind mount into container -func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDPair, dnsServers, dnsSearch, dnsOptions []string) (string, error) { +func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDPair, dnsServers, dnsSearch, dnsOptions []string, namespaceOptions NamespaceOptions) (string, error) { stat, err := os.Stat(hostPath) if err != nil { return "", errors.Wrapf(err, "error statting %q for container %q", hostPath, b.ContainerID) @@ -479,6 +476,16 @@ func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDP if len(dnsSearch) > 0 { search = dnsSearch } + + if b.Isolation == IsolationOCIRootless { + ns := namespaceOptions.Find(string(specs.NetworkNamespace)) + if ns != nil && !ns.Host && ns.Path == "" { + // if we are using slirp4netns, also add the built-in DNS server. + logrus.Debugf("adding slirp4netns 10.0.2.3 built-in DNS server") + nameservers = append([]string{"10.0.2.3"}, nameservers...) + } + } + if len(dnsServers) != 0 { dns, err := getDNSIP(dnsServers) if err != nil { @@ -589,7 +596,7 @@ func setupTerminal(g *generate.Generator, terminalPolicy TerminalPolicy, termina } } -func runUsingRuntime(isolation Isolation, options RunOptions, configureNetwork bool, configureNetworks, moreCreateArgs []string, spec *specs.Spec, rootPath, bundlePath, containerName string) (wstatus unix.WaitStatus, err error) { +func runUsingRuntime(isolation Isolation, options RunOptions, configureNetwork bool, configureNetworks, moreCreateArgs []string, spec *specs.Spec, bundlePath, containerName string) (wstatus unix.WaitStatus, err error) { // Lock the caller to a single OS-level thread. runtime.LockOSThread() @@ -944,8 +951,8 @@ func setupRootlessNetwork(pid int) (teardown func(), err error) { } return func() { - cmd.Process.Kill() - cmd.Wait() + cmd.Process.Kill() // nolint:errcheck + cmd.Wait() // nolint:errcheck }, nil } @@ -1131,17 +1138,17 @@ func runCopyStdio(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copy if err := setNonblock(rfd, readDesc[rfd], true); err != nil { return } - setNonblock(wfd, writeDesc[wfd], false) + setNonblock(wfd, writeDesc[wfd], false) // nolint:errcheck } if copyPipes { - setNonblock(stdioPipe[unix.Stdin][1], writeDesc[stdioPipe[unix.Stdin][1]], true) + setNonblock(stdioPipe[unix.Stdin][1], writeDesc[stdioPipe[unix.Stdin][1]], true) // nolint:errcheck } - runCopyStdioPassData(stdio, copyPipes, stdioPipe, copyConsole, consoleListener, finishCopy, finishedCopy, spec, relayMap, relayBuffer, readDesc, writeDesc) + runCopyStdioPassData(copyPipes, stdioPipe, finishCopy, relayMap, relayBuffer, readDesc, writeDesc) } -func runCopyStdioPassData(stdio *sync.WaitGroup, copyPipes bool, stdioPipe [][]int, copyConsole bool, consoleListener *net.UnixListener, finishCopy []int, finishedCopy chan struct{}, spec *specs.Spec, relayMap map[int]int, relayBuffer map[int]*bytes.Buffer, readDesc map[int]string, writeDesc map[int]string) { +func runCopyStdioPassData(copyPipes bool, stdioPipe [][]int, finishCopy []int, relayMap map[int]int, relayBuffer map[int]*bytes.Buffer, readDesc map[int]string, writeDesc map[int]string) { closeStdin := false // Pass data back and forth. @@ -1383,7 +1390,7 @@ func runUsingRuntimeMain() { os.Exit(1) } // Run the container, start to finish. - status, err := runUsingRuntime(options.Isolation, options.Options, options.ConfigureNetwork, options.ConfigureNetworks, options.MoreCreateArgs, options.Spec, options.RootPath, options.BundlePath, options.ContainerName) + status, err := runUsingRuntime(options.Isolation, options.Options, options.ConfigureNetwork, options.ConfigureNetworks, options.MoreCreateArgs, options.Spec, options.BundlePath, options.ContainerName) if err != nil { fmt.Fprintf(os.Stderr, "error running container: %v\n", err) os.Exit(1) @@ -1439,7 +1446,7 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i // If we've got mappings, we're going to have to create a user namespace. if len(idmapOptions.UIDMap) > 0 || len(idmapOptions.GIDMap) > 0 || configureUserns { - if err := g.AddOrReplaceLinuxNamespace(specs.UserNamespace, ""); err != nil { + if err := g.AddOrReplaceLinuxNamespace(string(specs.UserNamespace), ""); err != nil { return false, nil, false, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.UserNamespace)) } hostUidmap, hostGidmap, err := unshare.GetHostIDMappings("") @@ -1463,17 +1470,17 @@ func setupNamespaces(g *generate.Generator, namespaceOptions NamespaceOptions, i } } if !specifiedNetwork { - if err := g.AddOrReplaceLinuxNamespace(specs.NetworkNamespace, ""); err != nil { + if err := g.AddOrReplaceLinuxNamespace(string(specs.NetworkNamespace), ""); err != nil { return false, nil, false, errors.Wrapf(err, "error adding new %q namespace for run", string(specs.NetworkNamespace)) } configureNetwork = (policy != NetworkDisabled) } } else { - if err := g.RemoveLinuxNamespace(specs.UserNamespace); err != nil { + if err := g.RemoveLinuxNamespace(string(specs.UserNamespace)); err != nil { return false, nil, false, errors.Wrapf(err, "error removing %q namespace for run", string(specs.UserNamespace)) } if !specifiedNetwork { - if err := g.RemoveLinuxNamespace(specs.NetworkNamespace); err != nil { + if err := g.RemoveLinuxNamespace(string(specs.NetworkNamespace)); err != nil { return false, nil, false, errors.Wrapf(err, "error removing %q namespace for run", string(specs.NetworkNamespace)) } } @@ -1544,7 +1551,7 @@ func (b *Builder) configureNamespaces(g *generate.Generator, options RunOptions) return configureNetwork, configureNetworks, nil } -func runSetupBoundFiles(bundlePath string, bindFiles map[string]string) (mounts []specs.Mount, err error) { +func runSetupBoundFiles(bundlePath string, bindFiles map[string]string) (mounts []specs.Mount) { for dest, src := range bindFiles { options := []string{"rbind"} if strings.HasPrefix(src, bundlePath) { @@ -1557,7 +1564,7 @@ func runSetupBoundFiles(bundlePath string, bindFiles map[string]string) (mounts Options: options, }) } - return mounts, nil + return mounts } func addRlimits(ulimit []string, g *generate.Generator) error { @@ -1598,7 +1605,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, return nil, errors.Wrapf(err, "error cleaning up overlay content for %s", b.ContainerID) } - parseMount := func(host, container string, options []string) (specs.Mount, error) { + parseMount := func(mountType, host, container string, options []string) (specs.Mount, error) { var foundrw, foundro, foundz, foundZ, foundO bool var rootProp string for _, opt := range options { @@ -1641,18 +1648,22 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, if rootProp == "" { options = append(options, "private") } + if mountType != "tmpfs" { + mountType = "bind" + options = append(options, "rbind") + } return specs.Mount{ Destination: container, - Type: "bind", + Type: mountType, Source: host, - Options: append(options, "rbind"), + Options: options, }, nil } // Bind mount volumes specified for this particular Run() invocation for _, i := range optionMounts { logrus.Debugf("setting up mounted volume at %q", i.Destination) - mount, err := parseMount(i.Source, i.Destination, i.Options) + mount, err := parseMount(i.Type, i.Source, i.Destination, i.Options) if err != nil { return nil, err } @@ -1666,7 +1677,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, options = strings.Split(spliti[2], ",") } options = append(options, "rbind") - mount, err := parseMount(spliti[0], spliti[1], options) + mount, err := parseMount("bind", spliti[0], spliti[1], options) if err != nil { return nil, err } @@ -1878,7 +1889,7 @@ func (b *Builder) configureEnvironment(g *generate.Generator, options RunOptions } } -func setupRootlessSpecChanges(spec *specs.Spec, bundleDir string, rootUID, rootGID uint32, shmSize string) error { +func setupRootlessSpecChanges(spec *specs.Spec, bundleDir string, shmSize string) error { spec.Hostname = "" spec.Process.User.AdditionalGids = nil spec.Linux.Resources = nil diff --git a/vendor/github.com/containers/buildah/troubleshooting.md b/vendor/github.com/containers/buildah/troubleshooting.md index 4b13bf317..3e292a6b2 100644 --- a/vendor/github.com/containers/buildah/troubleshooting.md +++ b/vendor/github.com/containers/buildah/troubleshooting.md @@ -107,4 +107,24 @@ lstat /home/myusername/~: no such file or directory * Replace `~` with `$HOME` or the fully specified directory `/home/myusername`. * `$ sudo buildah push alpine oci:${HOME}/myalpine:latest` + + --- +### 5) Rootless buildah bud fails EPERM on NFS: + +NFS enforces file creation on different UIDs on the server side and does not understand User Namespace. +When a container root process like YUM attempts to create a file owned by a different UID, NFS Server denies the creation. +NFS is also a problem for the file locks when the storage is on it. + +#### Symptom +```console +$ buildah bud . +ERRO[0014] Error while applying layer: ApplyLayer exit status 1 stdout: stderr: open /root/.bash_logout: permission denied +error creating build container: Error committing the finished image: error adding layer with blob "sha256:a02a4930cb5d36f3290eb84f4bfa30668ef2e9fe3a1fb73ec015fc58b9958b17": ApplyLayer exit status 1 stdout: stderr: open /root/.bash_logout: permission denied +``` + +#### Solution +Choose one of the following: + * Setup containers/storage in a different directory, not on an NFS share. + * Otherwise just run buildah as root, via `sudo buildah` +---
\ No newline at end of file diff --git a/vendor/github.com/containers/buildah/util.go b/vendor/github.com/containers/buildah/util.go index ce21d2651..71505f19e 100644 --- a/vendor/github.com/containers/buildah/util.go +++ b/vendor/github.com/containers/buildah/util.go @@ -18,7 +18,7 @@ import ( "github.com/containers/storage/pkg/pools" "github.com/containers/storage/pkg/reexec" "github.com/containers/storage/pkg/system" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" @@ -182,7 +182,6 @@ func (b *Builder) copyFileWithTar(tarIDMappingOptions *IDMappingOptions, chownOp } pipeWriter.Close() pipeWriter = nil - return }(f) untar := b.untar(chownOpts, hasher) diff --git a/vendor/github.com/containers/buildah/util/util.go b/vendor/github.com/containers/buildah/util/util.go index 4736d7b77..bea54b7ad 100644 --- a/vendor/github.com/containers/buildah/util/util.go +++ b/vendor/github.com/containers/buildah/util/util.go @@ -136,7 +136,7 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto continue } middle := "" - if prefix, ok := RegistryDefaultPathPrefix[registry]; ok && strings.IndexRune(name, '/') == -1 { + if prefix, ok := RegistryDefaultPathPrefix[registry]; ok && !strings.ContainsRune(name, '/') { middle = prefix } candidate := path.Join(registry, middle, name) diff --git a/vendor/github.com/containers/buildah/vendor.conf b/vendor/github.com/containers/buildah/vendor.conf deleted file mode 100644 index 88148947a..000000000 --- a/vendor/github.com/containers/buildah/vendor.conf +++ /dev/null @@ -1,69 +0,0 @@ -github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 -github.com/blang/semver v3.5.0 -github.com/BurntSushi/toml v0.2.0 -github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d -github.com/containernetworking/cni v0.7.0-rc2 -github.com/containers/image v2.0.0 -github.com/cyphar/filepath-securejoin v0.2.1 -github.com/vbauerster/mpb v3.3.4 -github.com/mattn/go-isatty v0.0.4 -github.com/VividCortex/ewma v1.1.1 -github.com/containers/storage v1.12.10 -github.com/docker/distribution 5f6282db7d65e6d72ad7c2cc66310724a57be716 -github.com/docker/docker 54dddadc7d5d89fe0be88f76979f6f6ab0dede83 -github.com/docker/docker-credential-helpers v0.6.1 -github.com/docker/go-connections v0.4.0 -github.com/docker/go-units v0.3.2 -github.com/docker/libtrust aabc10ec26b754e797f9028f4589c5b7bd90dc20 -github.com/docker/libnetwork 1a06131fb8a047d919f7deaf02a4c414d7884b83 -github.com/fsouza/go-dockerclient v1.3.0 -github.com/ghodss/yaml v1.0.0 -github.com/gogo/protobuf v1.2.0 -github.com/gorilla/context v1.1.1 -github.com/gorilla/mux v1.6.2 -github.com/hashicorp/errwrap v1.0.0 -github.com/hashicorp/go-multierror v1.0.0 -github.com/imdario/mergo v0.3.6 -github.com/mattn/go-shellwords v1.0.3 -github.com/Microsoft/go-winio v0.4.11 -github.com/Microsoft/hcsshim v0.8.3 -github.com/mistifyio/go-zfs v2.1.1 -github.com/moby/moby f8806b18b4b92c5e1980f6e11c917fad201cd73c -github.com/mtrmac/gpgme b2432428689ca58c2b8e8dea9449d3295cf96fc9 -# TODO: Gotty has not been updated since 2012. Can we find a replacement? -github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512 -github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 -github.com/opencontainers/image-spec v1.0.0 -github.com/opencontainers/runc v1.0.0-rc6 -github.com/opencontainers/runtime-spec v1.0.0 -github.com/opencontainers/runtime-tools v0.8.0 -github.com/opencontainers/selinux v1.1 -github.com/openshift/imagebuilder v1.1.0 -github.com/ostreedev/ostree-go 9ab99253d365aac3a330d1f7281cf29f3d22820b -github.com/pkg/errors v0.8.1 -github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac -github.com/seccomp/libseccomp-golang v0.9.0 -github.com/seccomp/containers-golang v0.1 -github.com/sirupsen/logrus v1.0.0 -github.com/syndtr/gocapability d98352740cb2c55f81556b63d4a1ec64c5a319c2 -github.com/tchap/go-patricia v2.2.6 -github.com/ulikunitz/xz v0.5.5 -github.com/vbatts/tar-split v0.10.2 -github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 -github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b -github.com/xeipuuv/gojsonschema v1.1.0 -golang.org/x/crypto ff983b9c42bc9fbf91556e191cc8efb585c16908 https://github.com/golang/crypto -golang.org/x/net 45ffb0cd1ba084b73e26dee67e667e1be5acce83 https://github.com/golang/net -golang.org/x/sync 37e7f081c4d4c64e13b10787722085407fe5d15f https://github.com/golang/sync -golang.org/x/sys 7fbe1cd0fcc20051e1fcb87fbabec4a1bacaaeba https://github.com/golang/sys -golang.org/x/text e6919f6577db79269a6443b9dc46d18f2238fb5d https://github.com/golang/text -gopkg.in/yaml.v2 v2.2.2 -k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go -github.com/klauspost/pgzip v1.2.1 -github.com/klauspost/compress v1.4.1 -github.com/klauspost/cpuid v1.2.0 -github.com/onsi/gomega v1.4.3 -github.com/spf13/cobra v0.0.3 -github.com/spf13/pflag v1.0.3 -github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb -github.com/etcd-io/bbolt v1.3.2 diff --git a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go index 5cb251b13..946bb8712 100644 --- a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go +++ b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go @@ -14,6 +14,45 @@ import ( "github.com/sirupsen/logrus" ) +const ( + // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path(). + defaultPath = "/etc/resolv.conf" + // alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path(). + alternatePath = "/run/systemd/resolve/resolv.conf" +) + +var ( + detectSystemdResolvConfOnce sync.Once + pathAfterSystemdDetection = defaultPath +) + +// Path returns the path to the resolv.conf file that libnetwork should use. +// +// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then +// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53 +// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf +// which is the resolv.conf that systemd-resolved generates and manages. +// Otherwise Path() returns /etc/resolv.conf. +// +// Errors are silenced as they will inevitably resurface at future open/read calls. +// +// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf +func Path() string { + detectSystemdResolvConfOnce.Do(func() { + candidateResolvConf, err := ioutil.ReadFile(defaultPath) + if err != nil { + // silencing error as it will resurface at next calls trying to read defaultPath + return + } + ns := GetNameservers(candidateResolvConf, types.IP) + if len(ns) == 1 && ns[0] == "127.0.0.53" { + pathAfterSystemdDetection = alternatePath + logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) + } + }) + return pathAfterSystemdDetection +} + var ( // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} @@ -50,15 +89,7 @@ type File struct { // Get returns the contents of /etc/resolv.conf and its hash func Get() (*File, error) { - resolv, err := ioutil.ReadFile("/etc/resolv.conf") - if err != nil { - return nil, err - } - hash, err := ioutils.HashData(bytes.NewReader(resolv)) - if err != nil { - return nil, err - } - return &File{Content: resolv, Hash: hash}, nil + return GetSpecific(Path()) } // GetSpecific returns the contents of the user specified resolv.conf file and its hash @@ -81,7 +112,7 @@ func GetIfChanged() (*File, error) { lastModified.Lock() defer lastModified.Unlock() - resolv, err := ioutil.ReadFile("/etc/resolv.conf") + resolv, err := ioutil.ReadFile(Path()) if err != nil { return nil, err } diff --git a/vendor/github.com/docker/libnetwork/types/types.go b/vendor/github.com/docker/libnetwork/types/types.go index f851d6fbb..db1960c10 100644 --- a/vendor/github.com/docker/libnetwork/types/types.go +++ b/vendor/github.com/docker/libnetwork/types/types.go @@ -99,7 +99,7 @@ func (p PortBinding) HostAddr() (net.Addr, error) { case TCP: return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil case SCTP: - return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil default: return nil, ErrInvalidProtocolBinding(p.Proto.String()) } @@ -113,7 +113,7 @@ func (p PortBinding) ContainerAddr() (net.Addr, error) { case TCP: return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil case SCTP: - return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil default: return nil, ErrInvalidProtocolBinding(p.Proto.String()) } @@ -145,7 +145,12 @@ func (p *PortBinding) String() string { return ret } -// FromString reads the PortBinding structure from string +// FromString reads the PortBinding structure from string s. +// String s is a triple of "protocol/containerIP:port/hostIP:port" +// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form. +// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported. +// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString +// returns an error. func (p *PortBinding) FromString(s string) error { ps := strings.Split(s, "/") if len(ps) != 3 { @@ -167,21 +172,19 @@ func (p *PortBinding) FromString(s string) error { } func parseIPPort(s string) (net.IP, uint16, error) { - pp := strings.Split(s, ":") - if len(pp) != 2 { - return nil, 0, BadRequestErrorf("invalid format: %s", s) + hoststr, portstr, err := net.SplitHostPort(s) + if err != nil { + return nil, 0, err } - var ip net.IP - if pp[0] != "" { - if ip = net.ParseIP(pp[0]); ip == nil { - return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0]) - } + ip := net.ParseIP(hoststr) + if ip == nil { + return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr) } - port, err := strconv.ParseUint(pp[1], 10, 16) + port, err := strconv.ParseUint(portstr, 10, 16) if err != nil { - return nil, 0, BadRequestErrorf("invalid port: %s", pp[1]) + return nil, 0, BadRequestErrorf("invalid port: %s", portstr) } return ip, uint16(port), nil @@ -329,6 +332,8 @@ func CompareIPNet(a, b *net.IPNet) bool { } // GetMinimalIP returns the address in its shortest form +// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned. +// Otherwise ip is returned unchanged. func GetMinimalIP(ip net.IP) net.IP { if ip != nil && ip.To4() != nil { return ip.To4() diff --git a/vendor/github.com/ishidawataru/sctp/.gitignore b/vendor/github.com/ishidawataru/sctp/.gitignore index a1338d685..cf2d826c1 100644 --- a/vendor/github.com/ishidawataru/sctp/.gitignore +++ b/vendor/github.com/ishidawataru/sctp/.gitignore @@ -12,3 +12,5 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ + +example/example diff --git a/vendor/github.com/ishidawataru/sctp/.travis.yml b/vendor/github.com/ishidawataru/sctp/.travis.yml index 64f85ef28..e72c57864 100644 --- a/vendor/github.com/ishidawataru/sctp/.travis.yml +++ b/vendor/github.com/ishidawataru/sctp/.travis.yml @@ -4,6 +4,7 @@ go: - 1.7 - 1.8 - 1.9 + - "1.10" script: - go test -v -race ./... diff --git a/vendor/github.com/ishidawataru/sctp/GO_LICENSE b/vendor/github.com/ishidawataru/sctp/GO_LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/GO_LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/ishidawataru/sctp/ipsock_linux.go b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go new file mode 100644 index 000000000..f5632b72d --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go @@ -0,0 +1,218 @@ +package sctp + +import ( + "net" + "os" + "sync" + "syscall" +) + +//from https://github.com/golang/go +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +//from https://github.com/golang/go +func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = net.IPv4zero + } + ip4 := ip.To4() + if ip4 == nil { + return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()} + } + sa := &syscall.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return sa, nil + case syscall.AF_INET6: + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow an listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(net.IPv4zero) { + ip = net.IPv6zero + } + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { + return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()} + } + //we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host + //if real Zone handling is required, the zone cache implementation in golang/net should be pulled here + sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0} + copy(sa.Addr[:], ip6) + return sa, nil + } + return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()} +} + +//from https://github.com/golang/go +func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) { + if a == nil { + return nil, nil + } + return ipToSockaddr(family, a.IP, a.Port, a.Zone) +} + +//from https://github.com/golang/go +type ipStackCapabilities struct { + sync.Once // guards following + ipv4Enabled bool + ipv6Enabled bool + ipv4MappedIPv6Enabled bool +} + +//from https://github.com/golang/go +var ipStackCaps ipStackCapabilities + +//from https://github.com/golang/go +// supportsIPv4 reports whether the platform supports IPv4 networking +// functionality. +func supportsIPv4() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4Enabled +} + +//from https://github.com/golang/go +// supportsIPv6 reports whether the platform supports IPv6 networking +// functionality. +func supportsIPv6() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv6Enabled +} + +//from https://github.com/golang/go +// supportsIPv4map reports whether the platform supports mapping an +// IPv4 address inside an IPv6 address at transport layer +// protocols. See RFC 4291, RFC 4038 and RFC 3493. +func supportsIPv4map() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4MappedIPv6Enabled +} + +//from https://github.com/golang/go +// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication +// capabilities which are controlled by the IPV6_V6ONLY socket option +// and kernel configuration. +// +// Should we try to use the IPv4 socket interface if we're only +// dealing with IPv4 sockets? As long as the host system understands +// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to +// the IPv6 interface. That simplifies our code and is most +// general. Unfortunately, we need to run on kernels built without +// IPv6 support too. So probe the kernel to figure it out. +func (p *ipStackCapabilities) probe() { + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + switch err { + case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: + case nil: + syscall.Close(s) + p.ipv4Enabled = true + } + var probes = []struct { + laddr net.TCPAddr + value int + }{ + // IPv6 communication capability + {laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1}, + // IPv4-mapped IPv6 address communication capability + {laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0}, + } + + for i := range probes { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { + continue + } + defer syscall.Close(s) + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) + sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6) + if err != nil { + continue + } + if err := syscall.Bind(s, sa); err != nil { + continue + } + if i == 0 { + p.ipv6Enabled = true + } else { + p.ipv4MappedIPv6Enabled = true + } + } +} + +//from https://github.com/golang/go +//Change: we check the first IP address in the list of candidate SCTP IP addresses +func (a *SCTPAddr) isWildcard() bool { + if a == nil { + return true + } + if 0 == len(a.IPAddrs) { + return true + } + + return a.IPAddrs[0].IP.IsUnspecified() +} + +func (a *SCTPAddr) family() int { + if a != nil { + for _, ip := range a.IPAddrs { + if ip.IP.To4() == nil { + return syscall.AF_INET6 + } + } + } + return syscall.AF_INET +} + +//from https://github.com/golang/go +func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) { + switch network[len(network)-1] { + case '4': + return syscall.AF_INET, false + case '6': + return syscall.AF_INET6, true + } + + if mode == "listen" && (laddr == nil || laddr.isWildcard()) { + if supportsIPv4map() || !supportsIPv4() { + return syscall.AF_INET6, false + } + if laddr == nil { + return syscall.AF_INET, false + } + return laddr.family(), false + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET, false + } + return syscall.AF_INET6, false +} + +//from https://github.com/golang/go +//Changes: it is for SCTP only +func setDefaultSockopts(s int, family int, ipv6only bool) error { + if family == syscall.AF_INET6 { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only)) + } + // Allow broadcast. + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)) +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp.go b/vendor/github.com/ishidawataru/sctp/sctp.go index cac1a889c..30d619640 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp.go +++ b/vendor/github.com/ishidawataru/sctp/sctp.go @@ -197,37 +197,58 @@ func htons(h uint16) uint16 { var ntohs = htons -func setNumOstreams(fd, num int) error { - param := InitMsg{ - NumOstreams: uint16(num), - } - optlen := unsafe.Sizeof(param) - _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) +// setInitOpts sets options for an SCTP association initialization +// see https://tools.ietf.org/html/rfc4960#page-25 +func setInitOpts(fd int, options InitMsg) error { + optlen := unsafe.Sizeof(options) + _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen)) return err } +func setNumOstreams(fd, num int) error { + return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)}) +} + type SCTPAddr struct { - IP []net.IP - Port int + IPAddrs []net.IPAddr + Port int } func (a *SCTPAddr) ToRawSockAddrBuf() []byte { - buf := []byte{} p := htons(uint16(a.Port)) - for _, ip := range a.IP { - if ip.To4() != nil { + if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr + s := syscall.RawSockaddrInet4{ + Family: syscall.AF_INET, + Port: p, + } + copy(s.Addr[:], net.IPv4zero) + return toBuf(s) + } + buf := []byte{} + for _, ip := range a.IPAddrs { + ipBytes := ip.IP + if len(ipBytes) == 0 { + ipBytes = net.IPv4zero + } + if ip4 := ipBytes.To4(); ip4 != nil { s := syscall.RawSockaddrInet4{ Family: syscall.AF_INET, Port: p, } - copy(s.Addr[:], ip.To4()) + copy(s.Addr[:], ip4) buf = append(buf, toBuf(s)...) } else { + var scopeid uint32 + ifi, err := net.InterfaceByName(ip.Zone) + if err == nil { + scopeid = uint32(ifi.Index) + } s := syscall.RawSockaddrInet6{ - Family: syscall.AF_INET6, - Port: p, + Family: syscall.AF_INET6, + Port: p, + Scope_id: scopeid, } - copy(s.Addr[:], ip) + copy(s.Addr[:], ipBytes) buf = append(buf, toBuf(s)...) } } @@ -237,15 +258,15 @@ func (a *SCTPAddr) ToRawSockAddrBuf() []byte { func (a *SCTPAddr) String() string { var b bytes.Buffer - for n, i := range a.IP { - if a.IP[n].To4() != nil { + for n, i := range a.IPAddrs { + if i.IP.To4() != nil { b.WriteString(i.String()) - } else if a.IP[n].To16() != nil { + } else if i.IP.To16() != nil { b.WriteRune('[') b.WriteString(i.String()) b.WriteRune(']') } - if n < len(a.IP)-1 { + if n < len(a.IPAddrs)-1 { b.WriteRune('/') } } @@ -260,6 +281,7 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { tcpnet := "" switch network { case "", "sctp": + tcpnet = "tcp" case "sctp4": tcpnet = "tcp4" case "sctp6": @@ -271,26 +293,26 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { if len(elems) == 0 { return nil, fmt.Errorf("invalid input: %s", addrs) } - ipaddrs := make([]net.IP, 0, len(elems)) + ipaddrs := make([]net.IPAddr, 0, len(elems)) for _, e := range elems[:len(elems)-1] { tcpa, err := net.ResolveTCPAddr(tcpnet, e+":") if err != nil { return nil, err } - ipaddrs = append(ipaddrs, tcpa.IP) + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1]) if err != nil { return nil, err } if tcpa.IP != nil { - ipaddrs = append(ipaddrs, tcpa.IP) + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } else { ipaddrs = nil } return &SCTPAddr{ - IP: ipaddrs, - Port: tcpa.Port, + IPAddrs: ipaddrs, + Port: tcpa.Port, }, nil } @@ -357,15 +379,12 @@ func (c *SCTPConn) Read(b []byte) (int, error) { } func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error { - param := InitMsg{ + return setInitOpts(c.fd(), InitMsg{ NumOstreams: uint16(numOstreams), MaxInstreams: uint16(maxInstreams), MaxAttempts: uint16(maxAttempts), MaxInitTimeout: uint16(maxInitTimeout), - } - optlen := unsafe.Sizeof(param) - _, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) - return err + }) } func (c *SCTPConn) SubscribeEvents(flags int) error { @@ -473,7 +492,7 @@ func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) { func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { addr := &SCTPAddr{ - IP: make([]net.IP, n), + IPAddrs: make([]net.IPAddr, n), } switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family { @@ -484,7 +503,7 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) - addr.IP[i] = a.Addr[:] + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]} } case syscall.AF_INET6: addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) @@ -493,7 +512,12 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) - addr.IP[i] = a.Addr[:] + var zone string + ifi, err := net.InterfaceByIndex(int(a.Scope_id)) + if err == nil { + zone = ifi.Name + } + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone} } default: return nil, fmt.Errorf("unknown address family: %d", family) diff --git a/vendor/github.com/ishidawataru/sctp/sctp_linux.go b/vendor/github.com/ishidawataru/sctp/sctp_linux.go index f93ab8622..5a6ad9378 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp_linux.go +++ b/vendor/github.com/ishidawataru/sctp/sctp_linux.go @@ -3,7 +3,6 @@ package sctp import ( - "fmt" "io" "net" "sync/atomic" @@ -115,31 +114,14 @@ func (c *SCTPConn) Close() error { return syscall.EBADF } +// ListenSCTP - start listener on specified address/port func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { - af := syscall.AF_INET - switch net { - case "sctp": - hasv6 := func(addr *SCTPAddr) bool { - if addr == nil { - return false - } - for _, ip := range addr.IP { - if ip.To4() == nil { - return true - } - } - return false - } - if hasv6(laddr) { - af = syscall.AF_INET6 - } - case "sctp4": - case "sctp6": - af = syscall.AF_INET6 - default: - return nil, fmt.Errorf("invalid net: %s", net) - } + return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} +// ListenSCTPExt - start listener on specified address/port with given SCTP options +func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen") sock, err := syscall.Socket( af, syscall.SOCK_STREAM, @@ -148,11 +130,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { if err != nil { return nil, err } - err = setNumOstreams(sock, SCTP_MAX_STREAM) + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + err = setInitOpts(sock, options) if err != nil { return nil, err } - if laddr != nil && len(laddr.IP) != 0 { + + if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) if err != nil { return nil, err @@ -167,40 +168,30 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { }, nil } -func (ln *SCTPListener) Accept() (net.Conn, error) { +// AcceptSCTP waits for and returns the next SCTP connection to the listener. +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { fd, _, err := syscall.Accept4(ln.fd, 0) return NewSCTPConn(fd, nil), err } +// Accept waits for and returns the next connection connection to the listener. +func (ln *SCTPListener) Accept() (net.Conn, error) { + return ln.AcceptSCTP() +} + func (ln *SCTPListener) Close() error { syscall.Shutdown(ln.fd, syscall.SHUT_RDWR) return syscall.Close(ln.fd) } +// DialSCTP - bind socket to laddr (if given) and connect to raddr func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { - af := syscall.AF_INET - switch net { - case "sctp": - hasv6 := func(addr *SCTPAddr) bool { - if addr == nil { - return false - } - for _, ip := range addr.IP { - if ip.To4() == nil { - return true - } - } - return false - } - if hasv6(laddr) || hasv6(raddr) { - af = syscall.AF_INET6 - } - case "sctp4": - case "sctp6": - af = syscall.AF_INET6 - default: - return nil, fmt.Errorf("invalid net: %s", net) - } + return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} + +// DialSCTPExt - same as DialSCTP but with given SCTP options +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial") sock, err := syscall.Socket( af, syscall.SOCK_STREAM, @@ -209,11 +200,29 @@ func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { if err != nil { return nil, err } - err = setNumOstreams(sock, SCTP_MAX_STREAM) + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + err = setInitOpts(sock, options) if err != nil { return nil, err } if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) if err != nil { return nil, err diff --git a/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go index adcbf78b4..e5415843d 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go +++ b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go @@ -34,10 +34,18 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { return nil, ErrUnsupported } +func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + return nil, ErrUnsupported +} + func (ln *SCTPListener) Accept() (net.Conn, error) { return nil, ErrUnsupported } +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { + return nil, ErrUnsupported +} + func (ln *SCTPListener) Close() error { return ErrUnsupported } @@ -45,3 +53,7 @@ func (ln *SCTPListener) Close() error { func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { return nil, ErrUnsupported } + +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + return nil, ErrUnsupported +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index 316d963d6..9717acc72 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -14,6 +14,7 @@ import ( "time" units "github.com/docker/go-units" + "golang.org/x/sys/unix" ) const ( @@ -464,10 +465,39 @@ func WriteCgroupProc(dir string, pid int) error { } // Dont attach any pid to the cgroup if -1 is specified as a pid - if pid != -1 { - if err := ioutil.WriteFile(filepath.Join(dir, CgroupProcesses), []byte(strconv.Itoa(pid)), 0700); err != nil { - return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err) + if pid == -1 { + return nil + } + + cgroupProcessesFile, err := os.OpenFile(filepath.Join(dir, CgroupProcesses), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700) + if err != nil { + return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err) + } + defer cgroupProcessesFile.Close() + + for i := 0; i < 5; i++ { + _, err = cgroupProcessesFile.WriteString(strconv.Itoa(pid)) + if err == nil { + return nil } + + // EINVAL might mean that the task being added to cgroup.procs is in state + // TASK_NEW. We should attempt to do so again. + if isEINVAL(err) { + time.Sleep(30 * time.Millisecond) + continue + } + + return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err) + } + return err +} + +func isEINVAL(err error) bool { + switch err := err.(type) { + case *os.PathError: + return err.Err == unix.EINVAL + default: + return false } - return nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go index 6f47aac07..57e9f037d 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go @@ -5,7 +5,9 @@ type IntelRdt struct { // Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..." L3CacheSchema string `json:"l3_cache_schema,omitempty"` - // The schema of memory bandwidth percentage per L3 cache id + // The schema of memory bandwidth per L3 cache id // Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..." + // The unit of memory bandwidth is specified in "percentages" by + // default, and in "MBps" if MBA Software Controller is enabled. MemBwSchema string `json:"memBwSchema,omitempty"` } diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index 6d791e7e9..48e621c99 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -38,7 +38,9 @@ type Process struct { // User specifies user information for the process. User User `json:"user"` // Args specifies the binary and arguments for the application to execute. - Args []string `json:"args"` + Args []string `json:"args,omitempty"` + // CommandLine specifies the full command line for the application to execute on Windows. + CommandLine string `json:"commandLine,omitempty" platform:"windows"` // Env populates the process environment for the process. Env []string `json:"env,omitempty"` // Cwd is the current working directory for the process and must be @@ -181,17 +183,17 @@ const ( // PIDNamespace for isolating process IDs PIDNamespace LinuxNamespaceType = "pid" // NetworkNamespace for isolating network devices, stacks, ports, etc - NetworkNamespace = "network" + NetworkNamespace LinuxNamespaceType = "network" // MountNamespace for isolating mount points - MountNamespace = "mount" + MountNamespace LinuxNamespaceType = "mount" // IPCNamespace for isolating System V IPC, POSIX message queues - IPCNamespace = "ipc" + IPCNamespace LinuxNamespaceType = "ipc" // UTSNamespace for isolating hostname and NIS domain name - UTSNamespace = "uts" + UTSNamespace LinuxNamespaceType = "uts" // UserNamespace for isolating user and group IDs - UserNamespace = "user" + UserNamespace LinuxNamespaceType = "user" // CgroupNamespace for isolating cgroup hierarchies - CgroupNamespace = "cgroup" + CgroupNamespace LinuxNamespaceType = "cgroup" ) // LinuxIDMapping specifies UID/GID mappings @@ -217,6 +219,7 @@ type POSIXRlimit struct { // LinuxHugepageLimit structure corresponds to limiting kernel hugepages type LinuxHugepageLimit struct { // Pagesize is the hugepage size + // Format: "<size><unit-prefix>B' (e.g. 64KB, 2MB, 1GB, etc.) Pagesize string `json:"pageSize"` // Limit is the limit of "hugepagesize" hugetlb usage Limit uint64 `json:"limit"` diff --git a/vendor/github.com/vishvananda/netns/README.md b/vendor/github.com/vishvananda/netns/README.md index 66a5f7258..6b45cfb89 100644 --- a/vendor/github.com/vishvananda/netns/README.md +++ b/vendor/github.com/vishvananda/netns/README.md @@ -37,7 +37,6 @@ func main() { // Create a new network namespace newns, _ := netns.New() - netns.Set(newns) defer newns.Close() // Do something with the network namespace diff --git a/vendor/github.com/vishvananda/netns/netns_linux.go b/vendor/github.com/vishvananda/netns/netns_linux.go index e665ef449..b1e3b07c0 100644 --- a/vendor/github.com/vishvananda/netns/netns_linux.go +++ b/vendor/github.com/vishvananda/netns/netns_linux.go @@ -21,8 +21,10 @@ var SYS_SETNS = map[string]uintptr{ "arm": 375, "mips": 4344, "mipsle": 4344, + "mips64le": 4344, "ppc64": 350, "ppc64le": 350, + "riscv64": 268, "s390x": 339, }[runtime.GOARCH] @@ -52,7 +54,8 @@ func Set(ns NsHandle) (err error) { return Setns(ns, CLONE_NEWNET) } -// New creates a new network namespace and returns a handle to it. +// New creates a new network namespace, sets it as current and returns +// a handle to it. func New() (ns NsHandle, err error) { if err := syscall.Unshare(CLONE_NEWNET); err != nil { return -1, err |