mirror of
https://github.com/containers/podman.git
synced 2026-08-07 04:43:04 -04:00
vendor: bump containers/(storage, common, buildah, image)
Bump containers/(storage, common, buildah and image) Changes since 2023-01-01: - skip mount-cache-selinux-long-name test under remote, with a FIXME requesting that someone see if it can be made to work. - skip six tests that fail under rootless-remote - add new --build-arg-file option: - update man page Squash of: * https://github.com/containers/podman/pull/18106/commits/cf56eb18654cd8c4fd2d27a1cb96325b179ab010 * https://github.com/containers/podman/pull/18106/commits/561f082772e18eae7594dd148044f572084ea266 Signed-off-by: Ed Santiago <santiago@redhat.com> Signed-off-by: Daniel J Walsh <dwalsh@redhat.com> Signed-off-by: Aditya R <arajan@redhat.com>
This commit is contained in:
274 files changed
+19599
-1689
No files matched your search
+13
-12
@@ -29,12 +29,13 @@ env:
|
||||
IMAGE_PROJECT: "libpod-218412"
|
||||
FEDORA_NAME: "fedora-37"
|
||||
PRIOR_FEDORA_NAME: "fedora-36"
|
||||
UBUNTU_NAME: "ubuntu-2204"
|
||||
DEBIAN_NAME: "debian-12"
|
||||
|
||||
IMAGE_SUFFIX: "c6300530360713216"
|
||||
# Image identifiers
|
||||
IMAGE_SUFFIX: "c20230405t152256z-f37f36d12"
|
||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
|
||||
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
|
||||
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
|
||||
|
||||
IN_PODMAN_IMAGE: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}"
|
||||
|
||||
@@ -75,7 +76,7 @@ meta_task:
|
||||
IMGNAMES: |-
|
||||
${FEDORA_CACHE_IMAGE_NAME}
|
||||
${PRIOR_FEDORA_CACHE_IMAGE_NAME}
|
||||
${UBUNTU_CACHE_IMAGE_NAME}
|
||||
${DEBIAN_CACHE_IMAGE_NAME}
|
||||
build-push-${IMAGE_SUFFIX}
|
||||
BUILDID: "${CIRRUS_BUILD_ID}"
|
||||
REPOREF: "${CIRRUS_CHANGE_IN_REPO}"
|
||||
@@ -120,7 +121,7 @@ vendor_task:
|
||||
|
||||
# Runs within Cirrus's "community cluster"
|
||||
container:
|
||||
image: docker.io/library/golang:1.17
|
||||
image: docker.io/library/golang:1.18
|
||||
cpu: 1
|
||||
memory: 1
|
||||
|
||||
@@ -189,7 +190,7 @@ conformance_task:
|
||||
depends_on: *smoke_vendor_cross
|
||||
|
||||
gce_instance:
|
||||
image_name: "${UBUNTU_CACHE_IMAGE_NAME}"
|
||||
image_name: "${DEBIAN_CACHE_IMAGE_NAME}"
|
||||
|
||||
timeout_in: 25m
|
||||
|
||||
@@ -220,8 +221,8 @@ integration_task:
|
||||
IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
|
||||
STORAGE_DRIVER: 'vfs'
|
||||
- env:
|
||||
DISTRO_NV: "${UBUNTU_NAME}"
|
||||
IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}"
|
||||
DISTRO_NV: "${DEBIAN_NAME}"
|
||||
IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
|
||||
STORAGE_DRIVER: 'vfs'
|
||||
# OVERLAY
|
||||
- env:
|
||||
@@ -233,8 +234,8 @@ integration_task:
|
||||
IMAGE_NAME: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
|
||||
STORAGE_DRIVER: 'overlay'
|
||||
- env:
|
||||
DISTRO_NV: "${UBUNTU_NAME}"
|
||||
IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}"
|
||||
DISTRO_NV: "${DEBIAN_NAME}"
|
||||
IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
|
||||
STORAGE_DRIVER: 'overlay'
|
||||
|
||||
gce_instance:
|
||||
@@ -278,8 +279,8 @@ integration_rootless_task:
|
||||
STORAGE_DRIVER: 'overlay'
|
||||
PRIV_NAME: rootless
|
||||
- env:
|
||||
DISTRO_NV: "${UBUNTU_NAME}"
|
||||
IMAGE_NAME: "${UBUNTU_CACHE_IMAGE_NAME}"
|
||||
DISTRO_NV: "${DEBIAN_NAME}"
|
||||
IMAGE_NAME: "${DEBIAN_CACHE_IMAGE_NAME}"
|
||||
STORAGE_DRIVER: 'overlay'
|
||||
PRIV_NAME: rootless
|
||||
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script handles any custom processing of the spec file generated using the `post-upstream-clone`
|
||||
# action and gets used by the fix-spec-file action in .packit.yaml.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# Get Version from define/types.go in HEAD
|
||||
VERSION=$(grep ^$'\tVersion' define/types.go | cut -d\" -f2 | sed -e 's/-/~/')
|
||||
|
||||
# Generate source tarball from HEAD
|
||||
git archive --prefix=buildah-$VERSION/ -o buildah-$VERSION.tar.gz HEAD
|
||||
|
||||
# RPM Spec modifications
|
||||
|
||||
# Use the Version from define/types.go in rpm spec
|
||||
sed -i "s/^Version:.*/Version: $VERSION/" buildah.spec
|
||||
|
||||
# Use Packit's supplied variable in the Release field in rpm spec.
|
||||
# buildah.spec is generated using `rpkg spec --outdir ./` as mentioned in the
|
||||
# `post-upstream-clone` action in .packit.yaml.
|
||||
sed -i "s/^Release:.*/Release: $PACKIT_RPMSPEC_RELEASE%{?dist}/" buildah.spec
|
||||
|
||||
# Use above generated tarball as Source in rpm spec
|
||||
sed -i "s/^Source:.*.tar.gz/Source: buildah-$VERSION.tar.gz/" buildah.spec
|
||||
|
||||
# Use the right build dir for autosetup stage in rpm spec
|
||||
sed -i "s/^%setup.*/%autosetup -Sgit -n %{name}-$VERSION/" buildah.spec
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
---
|
||||
# See the documentation for more information:
|
||||
# https://packit.dev/docs/configuration/
|
||||
|
||||
# Build targets can be found at:
|
||||
# https://copr.fedorainfracloud.org/coprs/rhcontainerbot/packit-builds/
|
||||
|
||||
specfile_path: buildah.spec
|
||||
|
||||
jobs:
|
||||
- &copr
|
||||
job: copr_build
|
||||
trigger: pull_request
|
||||
owner: rhcontainerbot
|
||||
project: packit-builds
|
||||
enable_net: true
|
||||
srpm_build_deps:
|
||||
- make
|
||||
- rpkg
|
||||
actions:
|
||||
post-upstream-clone:
|
||||
- "rpkg spec --outdir ./"
|
||||
fix-spec-file:
|
||||
- "bash .packit.sh"
|
||||
|
||||
- <<: *copr
|
||||
# Run on commit to main branch
|
||||
trigger: commit
|
||||
branch: main
|
||||
project: podman-next
|
||||
+73
@@ -2,6 +2,79 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## v1.30.0 (2023-04-06)
|
||||
|
||||
fix(deps): update module github.com/opencontainers/runc to v1.1.5
|
||||
fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.7
|
||||
buildah image should not enable fuse-overlayfs for rootful mode
|
||||
stage_executor: inline network add default string
|
||||
fix(deps): update module github.com/containers/common to v0.51.2
|
||||
chore(deps): update dependency containers/automation_images to v20230330
|
||||
fix(deps): update module github.com/docker/docker to v23.0.2+incompatible
|
||||
chore(deps): update dependency containers/automation_images to v20230320
|
||||
fix(deps): update module github.com/onsi/gomega to v1.27.6
|
||||
fix(deps): update github.com/opencontainers/runtime-tools digest to e931285
|
||||
[skip-ci] Update actions/stale action to v8
|
||||
test: don't allow to override io.buildah.version
|
||||
executor: only apply label on the final stage
|
||||
Update docs/buildah-build.1.md
|
||||
update build instruction for Ubuntu
|
||||
code review
|
||||
build: accept arguments from file with --build-arg-file
|
||||
run_linux: Update heuristic for mounting /sys
|
||||
[CI:BUILD] Packit: Enable Copr builds on PR and commit to main
|
||||
fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.6
|
||||
Update to Go 1.18
|
||||
Disable dependabot in favor of renovate
|
||||
chore(deps): update dependency containers/automation_images to v20230314
|
||||
Fix requiring tests on Makefile changes
|
||||
Vendor in latest containers/(storage, common, image)
|
||||
imagebuildah: set len(short_image_id) to 12
|
||||
Re-enable conformance tests
|
||||
Skip conformance test failures with Docker 23.0.1
|
||||
Cirrus: Replace Ubuntu -> Debian SID
|
||||
run: add support for inline --network in RUN stmt
|
||||
vendor: bump imagebuilder to a3c3f8358ca31b1e4daa6
|
||||
stage_executor: attempt to push cache only when cacheKey is valid
|
||||
Add "ifnewer" as option in help message for pull command
|
||||
build: document behaviour of buildah's distributed cache
|
||||
fix(deps): update module golang.org/x/term to v0.6.0
|
||||
Add default list of capabilities required to run buildah in a container
|
||||
executor,copy: honor default ARG value while eval stage
|
||||
sshagent: use ExtendedAgent instead of Agent
|
||||
tests/bud: remove unwated test
|
||||
executor: do not warn on builtin default args
|
||||
executor: don't warn about unused TARGETARCH,TARGETOS,TARGETPLATFORM
|
||||
Fix tutorial for rootless mode
|
||||
Vendor in latest containers/(common, storage, image)
|
||||
Ignore the base image's base image annotations
|
||||
fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.5
|
||||
build(deps): bump github.com/containers/storage from 1.45.3 to 1.45.4
|
||||
Vendor in latest containers/common
|
||||
docs/tutorials/04: add defaults for Run()
|
||||
imagebuildah.StageExecutor: suppress bogus "Pushing cache []:..."
|
||||
executor: also add stage with no children to cleanupStages
|
||||
[CI:BUILD] copr: fix el8 builds
|
||||
Fix documentation on which Capabilities are allowed by default
|
||||
Skip subject-length validation for renovate PRs
|
||||
Temporarily hard-skip bud-multiple-platform-values test
|
||||
fix(deps): update github.com/openshift/imagebuilder digest to 86828bf
|
||||
build(deps): bump github.com/containerd/containerd from 1.6.16 to 1.6.17
|
||||
build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
|
||||
build(deps): bump github.com/containers/image/v5 from 5.24.0 to 5.24.1
|
||||
[skip-ci] Update tim-actions/get-pr-commits digest to 55b867b
|
||||
build(deps): bump github.com/opencontainers/selinux
|
||||
build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0
|
||||
Add renovate configuration
|
||||
Run codespell on codebase
|
||||
login: support interspersed args for password
|
||||
conformance: use scratch for minimal test
|
||||
pkg/parse: expose public CleanCacheMount API
|
||||
build(deps): bump go.etcd.io/bbolt from 1.3.6 to 1.3.7
|
||||
build(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16
|
||||
docs: specify order preference for FROM
|
||||
Bump to v1.30.0-dev
|
||||
|
||||
## v1.29.0 (2023-01-25)
|
||||
|
||||
tests: improve build-with-network-test
|
||||
|
||||
+2
-2
@@ -185,11 +185,11 @@ test-unit: tests/testreport/testreport
|
||||
$(GO_TEST) -v -tags "$(STORAGETAGS) $(SECURITYTAGS)" -cover $(RACEFLAGS) ./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
|
||||
|
||||
vendor-in-container:
|
||||
podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:1.17 make vendor
|
||||
podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:1.18 make vendor
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
GO111MODULE=on $(GO) mod tidy -compat=1.17
|
||||
GO111MODULE=on $(GO) mod tidy
|
||||
GO111MODULE=on $(GO) mod vendor
|
||||
GO111MODULE=on $(GO) mod verify
|
||||
|
||||
|
||||
+7
-2
@@ -26,8 +26,7 @@ const (
|
||||
// Package is the name of this package, used in help output and to
|
||||
// identify working containers.
|
||||
Package = define.Package
|
||||
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
||||
// too.
|
||||
// Version for the Package.
|
||||
Version = define.Version
|
||||
// The value we use to identify what type of information, currently a
|
||||
// serialized Builder structure, we are using as per-container state.
|
||||
@@ -350,6 +349,12 @@ type BuilderOptions struct {
|
||||
ProcessLabel string
|
||||
// MountLabel is the SELinux mount label associated with the container
|
||||
MountLabel string
|
||||
// PreserveBaseImageAnn[otation]s indicates that we should preserve base
|
||||
// image information that was present in our base image, instead of
|
||||
// overwriting them with information about the base image itself. This
|
||||
// is mainly useful as an internal implementation detail of multistage
|
||||
// builds, and does not need to be set by most callers.
|
||||
PreserveBaseImageAnns bool
|
||||
}
|
||||
|
||||
// ImportOptions are used to initialize a Builder from an existing container
|
||||
|
||||
+10
-13
@@ -11,6 +11,12 @@
|
||||
|
||||
%global with_debug 1
|
||||
|
||||
# RHEL 8's default %%gobuild macro doesn't account for the BUILDTAGS variable, so we
|
||||
# set it separately here and do not depend on RHEL 8's go-srpm-macros package.
|
||||
%if !0%{?fedora} && 0%{?rhel} <= 8
|
||||
%define gobuild(o:) GO111MODULE=off go build -buildmode pie -compiler gc -tags="rpm_crashtraceback libtrust_openssl ${BUILDTAGS:-}" -ldflags "-linkmode=external -compressdwarf=false ${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '%__global_ldflags'" -a -v -x %{?**};
|
||||
%endif
|
||||
|
||||
%if 0%{?with_debug}
|
||||
%global _find_debuginfo_dwz_opts %{nil}
|
||||
%global _dwz_low_mem_die_limit 0
|
||||
@@ -18,10 +24,6 @@
|
||||
%global debug_package %{nil}
|
||||
%endif
|
||||
|
||||
%if ! 0%{?gobuild:1}
|
||||
%define gobuild(o:) GO111MODULE=off go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**};
|
||||
%endif
|
||||
|
||||
%global provider github
|
||||
%global provider_tld com
|
||||
%global project containers
|
||||
@@ -56,11 +58,7 @@ BuildRequires: shadow-utils-subid-devel
|
||||
%if 0%{?fedora} && ! 0%{?rhel}
|
||||
BuildRequires: btrfs-progs-devel
|
||||
%endif
|
||||
%if 0%{?fedora} <= 35
|
||||
Requires: containers-common >= 4:1-39
|
||||
%else
|
||||
Requires: containers-common-extra
|
||||
%endif
|
||||
Requires: containers-common-extra >= 4:1-78
|
||||
%if 0%{?rhel}
|
||||
BuildRequires: libseccomp-devel
|
||||
%else
|
||||
@@ -68,7 +66,6 @@ BuildRequires: libseccomp-static
|
||||
%endif
|
||||
Requires: libseccomp
|
||||
Suggests: cpp
|
||||
Suggests: qemu-user-static
|
||||
|
||||
%description
|
||||
The %{name} package provides a command line tool which can be used to
|
||||
@@ -102,7 +99,6 @@ This package contains system tests for %{name}
|
||||
|
||||
%build
|
||||
%set_build_flags
|
||||
export GO111MODULE=off
|
||||
export GOPATH=$(pwd)/_build:$(pwd)
|
||||
export CGO_CFLAGS=$CFLAGS
|
||||
# These extra flags present in $CFLAGS have been skipped for now as they break the build
|
||||
@@ -113,6 +109,7 @@ CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-an
|
||||
%ifarch x86_64
|
||||
export CGO_CFLAGS+=" -m64 -mtune=generic -fcf-protection=full"
|
||||
%endif
|
||||
|
||||
mkdir _build
|
||||
pushd _build
|
||||
mkdir -p src/%{provider}.%{provider_tld}/%{project}
|
||||
@@ -124,9 +121,9 @@ mv vendor src
|
||||
export CNI_VERSION=`grep '^# github.com/containernetworking/cni ' src/modules.txt | sed 's,.* ,,'`
|
||||
export LDFLAGS="-X main.buildInfo=`date +%s` -X main.cniVersion=${CNI_VERSION}"
|
||||
|
||||
export BUILDTAGS='seccomp libsubid selinux'
|
||||
export BUILDTAGS="$(hack/libsubid_tag.sh) seccomp selinux $(hack/systemd_tag.sh)"
|
||||
%if 0%{?rhel}
|
||||
export BUILDTAGS='$BUILDTAGS exclude_graphdriver_btrfs btrfs_noversion'
|
||||
export BUILDTAGS="$BUILDTAGS exclude_graphdriver_btrfs btrfs_noversion"
|
||||
%endif
|
||||
|
||||
%gobuild -o bin/%{name} %{import_path}/cmd/%{name}
|
||||
|
||||
+72
@@ -1,3 +1,75 @@
|
||||
- Changelog for v1.30.0 (2023-04-06)
|
||||
* fix(deps): update module github.com/opencontainers/runc to v1.1.5
|
||||
* fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.7
|
||||
* buildah image should not enable fuse-overlayfs for rootful mode
|
||||
* stage_executor: inline network add default string
|
||||
* fix(deps): update module github.com/containers/common to v0.51.2
|
||||
* chore(deps): update dependency containers/automation_images to v20230330
|
||||
* fix(deps): update module github.com/docker/docker to v23.0.2+incompatible
|
||||
* chore(deps): update dependency containers/automation_images to v20230320
|
||||
* fix(deps): update module github.com/onsi/gomega to v1.27.6
|
||||
* fix(deps): update github.com/opencontainers/runtime-tools digest to e931285
|
||||
* [skip-ci] Update actions/stale action to v8
|
||||
* test: don't allow to override io.buildah.version
|
||||
* executor: only apply label on the final stage
|
||||
* Update docs/buildah-build.1.md
|
||||
* update build instruction for Ubuntu
|
||||
* code review
|
||||
* build: accept arguments from file with --build-arg-file
|
||||
* run_linux: Update heuristic for mounting /sys
|
||||
* [CI:BUILD] Packit: Enable Copr builds on PR and commit to main
|
||||
* fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.6
|
||||
* Update to Go 1.18
|
||||
* Disable dependabot in favor of renovate
|
||||
* chore(deps): update dependency containers/automation_images to v20230314
|
||||
* Fix requiring tests on Makefile changes
|
||||
* Vendor in latest containers/(storage, common, image)
|
||||
* imagebuildah: set len(short_image_id) to 12
|
||||
* Re-enable conformance tests
|
||||
* Skip conformance test failures with Docker 23.0.1
|
||||
* Cirrus: Replace Ubuntu -> Debian SID
|
||||
* run: add support for inline --network in RUN stmt
|
||||
* vendor: bump imagebuilder to a3c3f8358ca31b1e4daa6
|
||||
* stage_executor: attempt to push cache only when cacheKey is valid
|
||||
* Add "ifnewer" as option in help message for pull command
|
||||
* build: document behaviour of buildah's distributed cache
|
||||
* fix(deps): update module golang.org/x/term to v0.6.0
|
||||
* Add default list of capabilities required to run buildah in a container
|
||||
* executor,copy: honor default ARG value while eval stage
|
||||
* sshagent: use ExtendedAgent instead of Agent
|
||||
* tests/bud: remove unwated test
|
||||
* executor: do not warn on builtin default args
|
||||
* executor: don't warn about unused TARGETARCH,TARGETOS,TARGETPLATFORM
|
||||
* Fix tutorial for rootless mode
|
||||
* Vendor in latest containers/(common, storage, image)
|
||||
* Ignore the base image's base image annotations
|
||||
* fix(deps): update module github.com/fsouza/go-dockerclient to v1.9.5
|
||||
* build(deps): bump github.com/containers/storage from 1.45.3 to 1.45.4
|
||||
* Vendor in latest containers/common
|
||||
* docs/tutorials/04: add defaults for Run()
|
||||
* imagebuildah.StageExecutor: suppress bogus "Pushing cache []:..."
|
||||
* executor: also add stage with no children to cleanupStages
|
||||
* [CI:BUILD] copr: fix el8 builds
|
||||
* Fix documentation on which Capabilities are allowed by default
|
||||
* Skip subject-length validation for renovate PRs
|
||||
* Temporarily hard-skip bud-multiple-platform-values test
|
||||
* fix(deps): update github.com/openshift/imagebuilder digest to 86828bf
|
||||
* build(deps): bump github.com/containerd/containerd from 1.6.16 to 1.6.17
|
||||
* build(deps): bump tim-actions/get-pr-commits from 1.1.0 to 1.2.0
|
||||
* build(deps): bump github.com/containers/image/v5 from 5.24.0 to 5.24.1
|
||||
* [skip-ci] Update tim-actions/get-pr-commits digest to 55b867b
|
||||
* build(deps): bump github.com/opencontainers/selinux
|
||||
* build(deps): bump golang.org/x/crypto from 0.5.0 to 0.6.0
|
||||
* Add renovate configuration
|
||||
* Run codespell on codebase
|
||||
* login: support interspersed args for password
|
||||
* conformance: use scratch for minimal test
|
||||
* pkg/parse: expose public CleanCacheMount API
|
||||
* build(deps): bump go.etcd.io/bbolt from 1.3.6 to 1.3.7
|
||||
* build(deps): bump github.com/containerd/containerd from 1.6.15 to 1.6.16
|
||||
* docs: specify order preference for FROM
|
||||
* Bump to v1.30.0-dev
|
||||
|
||||
- Changelog for v1.29.0 (2023-01-25)
|
||||
* tests: improve build-with-network-test
|
||||
* Bump c/storagev1.45.3, c/imagev5.24.0, c/commonv0.51.0
|
||||
|
||||
+1
-7
@@ -92,13 +92,7 @@ func (b *Builder) initConfig(ctx context.Context, img types.Image, sys *types.Sy
|
||||
return fmt.Errorf("parsing OCI manifest %q: %w", string(b.Manifest), err)
|
||||
}
|
||||
for k, v := range v1Manifest.Annotations {
|
||||
// NOTE: do not override annotations that are
|
||||
// already set. Otherwise, we may erase
|
||||
// annotations such as the digest of the base
|
||||
// image.
|
||||
if value := b.ImageAnnotations[k]; value == "" {
|
||||
b.ImageAnnotations[k] = v
|
||||
}
|
||||
b.ImageAnnotations[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -28,9 +28,8 @@ const (
|
||||
// Package is the name of this package, used in help output and to
|
||||
// identify working containers.
|
||||
Package = "buildah"
|
||||
// Version for the Package. Bump version in contrib/rpm/buildah.spec
|
||||
// too.
|
||||
Version = "1.30.0-dev"
|
||||
// Version for the Package. Also used by .packit.sh for Packit builds.
|
||||
Version = "1.30.0"
|
||||
|
||||
// DefaultRuntime if containers.conf fails.
|
||||
DefaultRuntime = "runc"
|
||||
|
||||
-29
@@ -419,35 +419,6 @@ func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logr
|
||||
mainNode.Children = append(mainNode.Children, additionalNode.Children...)
|
||||
}
|
||||
|
||||
// Check if any labels were passed in via the API, and add a final line
|
||||
// to the Dockerfile that would provide the same result.
|
||||
// Reason: Docker adds label modification as a last step which can be
|
||||
// processed like regular steps, and if no modification is done to
|
||||
// layers, its easier to re-use cached layers.
|
||||
if len(options.Labels) > 0 {
|
||||
var labelLine string
|
||||
labels := append([]string{}, options.Labels...)
|
||||
for _, labelSpec := range labels {
|
||||
label := strings.SplitN(labelSpec, "=", 2)
|
||||
key := label[0]
|
||||
value := ""
|
||||
if len(label) > 1 {
|
||||
value = label[1]
|
||||
}
|
||||
// check only for an empty key since docker allows empty values
|
||||
if key != "" {
|
||||
labelLine += fmt.Sprintf(" %q=%q", key, value)
|
||||
}
|
||||
}
|
||||
if len(labelLine) > 0 {
|
||||
additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader("LABEL" + labelLine + "\n"))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("while adding additional LABEL step: %w", err)
|
||||
}
|
||||
mainNode.Children = append(mainNode.Children, additionalNode.Children...)
|
||||
}
|
||||
}
|
||||
|
||||
exec, err := newExecutor(logger, logPrefix, store, options, mainNode, containerFiles)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("creating build executor: %w", err)
|
||||
|
||||
+56
-12
@@ -43,14 +43,18 @@ import (
|
||||
// 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,
|
||||
"HTTP_PROXY": true,
|
||||
"http_proxy": true,
|
||||
"HTTPS_PROXY": true,
|
||||
"https_proxy": true,
|
||||
"FTP_PROXY": true,
|
||||
"ftp_proxy": true,
|
||||
"NO_PROXY": true,
|
||||
"no_proxy": true,
|
||||
"TARGETARCH": true,
|
||||
"TARGETOS": true,
|
||||
"TARGETPLATFORM": true,
|
||||
"TARGETVARIANT": true,
|
||||
}
|
||||
|
||||
// Executor is a buildah-based implementation of the imagebuilder.Executor
|
||||
@@ -467,6 +471,34 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE
|
||||
if stageIndex == len(stages)-1 {
|
||||
output = b.output
|
||||
}
|
||||
// Check if any labels were passed in via the API, and add a final line
|
||||
// to the Dockerfile that would provide the same result.
|
||||
// Reason: Docker adds label modification as a last step which can be
|
||||
// processed like regular steps, and if no modification is done to
|
||||
// layers, its easier to re-use cached layers.
|
||||
if len(b.labels) > 0 {
|
||||
var labelLine string
|
||||
labels := append([]string{}, b.labels...)
|
||||
for _, labelSpec := range labels {
|
||||
label := strings.SplitN(labelSpec, "=", 2)
|
||||
key := label[0]
|
||||
value := ""
|
||||
if len(label) > 1 {
|
||||
value = label[1]
|
||||
}
|
||||
// check only for an empty key since docker allows empty values
|
||||
if key != "" {
|
||||
labelLine += fmt.Sprintf(" %q=%q", key, value)
|
||||
}
|
||||
}
|
||||
if len(labelLine) > 0 {
|
||||
additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader("LABEL" + labelLine + "\n"))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("while adding additional LABEL step: %w", err)
|
||||
}
|
||||
stage.Node.Children = append(stage.Node.Children, additionalNode.Children...)
|
||||
}
|
||||
}
|
||||
|
||||
// If this stage is starting out with environment variables that were
|
||||
// passed in via our API, we should include them in the history, since
|
||||
@@ -480,8 +512,7 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE
|
||||
value := env[1]
|
||||
envLine += fmt.Sprintf(" %q=%q", key, value)
|
||||
} else {
|
||||
value := os.Getenv(key)
|
||||
envLine += fmt.Sprintf(" %q=%q", key, value)
|
||||
return "", nil, fmt.Errorf("BUG: unresolved environment variable: %q", key)
|
||||
}
|
||||
}
|
||||
if len(envLine) > 0 {
|
||||
@@ -521,8 +552,7 @@ func (b *Executor) buildStage(ctx context.Context, cleanupStages map[int]*StageE
|
||||
// build and b.forceRmIntermediateCtrs is set, make sure we
|
||||
// remove the intermediate/build containers, regardless of
|
||||
// whether or not the stage's build fails.
|
||||
// Skip cleanup if the stage has no instructions.
|
||||
if b.forceRmIntermediateCtrs || !b.layers && len(stage.Node.Children) > 0 {
|
||||
if b.forceRmIntermediateCtrs || !b.layers {
|
||||
b.stagesLock.Lock()
|
||||
cleanupStages[stage.Position] = stageExecutor
|
||||
b.stagesLock.Unlock()
|
||||
@@ -591,6 +621,9 @@ func (b *Executor) warnOnUnsetBuildArgs(stages imagebuilder.Stages, dependencyMa
|
||||
shouldWarn = false
|
||||
}
|
||||
}
|
||||
if _, isBuiltIn := builtinAllowedBuildArgs[argName]; isBuiltIn {
|
||||
shouldWarn = false
|
||||
}
|
||||
if shouldWarn {
|
||||
b.logger.Warnf("missing %q build argument. Try adding %q to the command line", argName, fmt.Sprintf("--build-arg %s=<VALUE>", argName))
|
||||
}
|
||||
@@ -754,6 +787,17 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
||||
// if following ADD or COPY needs any other
|
||||
// stage.
|
||||
stageName := rootfs
|
||||
headingArgs := argsMapToSlice(stage.Builder.HeadingArgs)
|
||||
userArgs := argsMapToSlice(stage.Builder.Args)
|
||||
// append heading args so if --build-arg key=value is not
|
||||
// specified but default value is set in Containerfile
|
||||
// via `ARG key=value` so default value can be used.
|
||||
userArgs = append(headingArgs, userArgs...)
|
||||
baseWithArg, err := imagebuilder.ProcessWord(stageName, userArgs)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("while replacing arg variables with values for format %q: %w", stageName, err)
|
||||
}
|
||||
stageName = baseWithArg
|
||||
// If --from=<index> convert index to name
|
||||
if index, err := strconv.Atoi(stageName); err == nil {
|
||||
stageName = stages[index].Name
|
||||
|
||||
+42
-33
@@ -599,6 +599,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error {
|
||||
defer devNull.Close()
|
||||
stdin = devNull
|
||||
}
|
||||
namespaceOptions := append([]define.NamespaceOption{}, s.executor.namespaceOptions...)
|
||||
options := buildah.RunOptions{
|
||||
Args: s.executor.runtimeArgs,
|
||||
Cmd: config.Cmd,
|
||||
@@ -609,7 +610,7 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error {
|
||||
Hostname: config.Hostname,
|
||||
Logger: s.executor.logger,
|
||||
Mounts: append([]Mount{}, s.executor.transientMounts...),
|
||||
NamespaceOptions: s.executor.namespaceOptions,
|
||||
NamespaceOptions: namespaceOptions,
|
||||
NoHosts: s.executor.noHosts,
|
||||
NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "",
|
||||
Quiet: s.executor.quiet,
|
||||
@@ -627,6 +628,19 @@ func (s *StageExecutor) Run(run imagebuilder.Run, config docker.Config) error {
|
||||
WorkingDir: config.WorkingDir,
|
||||
}
|
||||
|
||||
// Honor `RUN --network=<>`.
|
||||
switch run.Network {
|
||||
case "host":
|
||||
options.NamespaceOptions.AddOrReplace(define.NamespaceOption{Name: "network", Host: true})
|
||||
options.ConfigureNetwork = define.NetworkEnabled
|
||||
case "none":
|
||||
options.ConfigureNetwork = define.NetworkDisabled
|
||||
case "", "default":
|
||||
// do nothing
|
||||
default:
|
||||
return fmt.Errorf(`unsupported value %q for "RUN --network", must be either "host" or "none"`, run.Network)
|
||||
}
|
||||
|
||||
if config.NetworkDisabled {
|
||||
options.ConfigureNetwork = buildah.NetworkDisabled
|
||||
}
|
||||
@@ -678,7 +692,7 @@ func (s *StageExecutor) UnrecognizedInstruction(step *imagebuilder.Step) error {
|
||||
// prepare creates a working container based on the specified image, or if one
|
||||
// isn't specified, the first argument passed to the first FROM instruction we
|
||||
// can find in the stage's parsed tree.
|
||||
func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBConfig, rebase bool, pullPolicy define.PullPolicy) (builder *buildah.Builder, err error) {
|
||||
func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBConfig, rebase, preserveBaseImageAnnotations bool, pullPolicy define.PullPolicy) (builder *buildah.Builder, err error) {
|
||||
stage := s.stage
|
||||
ib := stage.Builder
|
||||
node := stage.Node
|
||||
@@ -753,6 +767,7 @@ func (s *StageExecutor) prepare(ctx context.Context, from string, initializeIBCo
|
||||
Logger: s.executor.logger,
|
||||
ProcessLabel: s.executor.processLabel,
|
||||
MountLabel: s.executor.mountLabel,
|
||||
PreserveBaseImageAnns: preserveBaseImageAnnotations,
|
||||
}
|
||||
|
||||
builder, err = buildah.NewBuilder(ctx, s.executor.store, builderOptions)
|
||||
@@ -865,7 +880,7 @@ func (s *StageExecutor) getImageRootfs(ctx context.Context, image string) (mount
|
||||
if builder, ok := s.executor.containerMap[image]; ok {
|
||||
return builder.MountPoint, nil
|
||||
}
|
||||
builder, err := s.prepare(ctx, image, false, false, s.executor.pullPolicy)
|
||||
builder, err := s.prepare(ctx, image, false, false, false, s.executor.pullPolicy)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -912,9 +927,11 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
}
|
||||
pullPolicy := s.executor.pullPolicy
|
||||
s.executor.stagesLock.Lock()
|
||||
var preserveBaseImageAnnotationsAtStageStart bool
|
||||
if stageImage, isPreviousStage := s.executor.imageMap[base]; isPreviousStage {
|
||||
base = stageImage
|
||||
pullPolicy = define.PullNever
|
||||
preserveBaseImageAnnotationsAtStageStart = true
|
||||
}
|
||||
s.executor.stagesLock.Unlock()
|
||||
|
||||
@@ -945,7 +962,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// 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, base, true, true, pullPolicy); err != nil {
|
||||
if _, err := s.prepare(ctx, base, true, true, preserveBaseImageAnnotationsAtStageStart, pullPolicy); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
children := stage.Node.Children
|
||||
@@ -968,18 +985,18 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
}
|
||||
// logCachePulled produces build log for cases when `--cache-from`
|
||||
// is used and a valid intermediate image is pulled from remote source.
|
||||
logCachePulled := func(cacheKey string) {
|
||||
logCachePulled := func(cacheKey string, remote reference.Named) {
|
||||
if !s.executor.quiet {
|
||||
cacheHitMessage := "--> Cache pulled from remote"
|
||||
fmt.Fprintf(s.executor.out, "%s %s\n", cacheHitMessage, fmt.Sprintf("%s:%s", s.executor.cacheFrom, cacheKey))
|
||||
cachePullMessage := "--> Cache pulled from remote"
|
||||
fmt.Fprintf(s.executor.out, "%s %s\n", cachePullMessage, fmt.Sprintf("%s:%s", remote.String(), cacheKey))
|
||||
}
|
||||
}
|
||||
// logCachePush produces build log for cases when `--cache-to`
|
||||
// is used and a valid intermediate image is pushed tp remote source.
|
||||
logCachePush := func(cacheKey string) {
|
||||
if !s.executor.quiet {
|
||||
cacheHitMessage := "--> Pushing cache"
|
||||
fmt.Fprintf(s.executor.out, "%s %s\n", cacheHitMessage, fmt.Sprintf("%s:%s", s.executor.cacheTo, cacheKey))
|
||||
cachePushMessage := "--> Pushing cache"
|
||||
fmt.Fprintf(s.executor.out, "%s %s\n", cachePushMessage, fmt.Sprintf("%s:%s", s.executor.cacheTo, cacheKey))
|
||||
}
|
||||
}
|
||||
logCacheHit := func(cacheID string) {
|
||||
@@ -989,8 +1006,8 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
}
|
||||
}
|
||||
logImageID := func(imgID string) {
|
||||
if len(imgID) > 11 {
|
||||
imgID = imgID[0:11]
|
||||
if len(imgID) > 12 {
|
||||
imgID = imgID[:12]
|
||||
}
|
||||
if s.executor.iidfile == "" {
|
||||
fmt.Fprintf(s.executor.out, "--> %s\n", imgID)
|
||||
@@ -1236,7 +1253,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
}
|
||||
}
|
||||
|
||||
needsCacheKey := (s.executor.cacheFrom != nil || s.executor.cacheTo != nil) && !avoidLookingCache
|
||||
needsCacheKey := (len(s.executor.cacheFrom) != 0 || len(s.executor.cacheTo) != 0) && !avoidLookingCache
|
||||
|
||||
// If we have to commit for this instruction, only assign the
|
||||
// stage's configured output name to the last layer.
|
||||
@@ -1289,12 +1306,12 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
}
|
||||
// All the best effort to find cache on localstorage have failed try pulling
|
||||
// cache from remote repo if `--cache-from` was configured.
|
||||
if cacheID == "" && s.executor.cacheFrom != nil {
|
||||
if cacheID == "" && len(s.executor.cacheFrom) != 0 {
|
||||
// only attempt to use cache again if pulling was successful
|
||||
// otherwise do nothing and attempt to run the step, err != nil
|
||||
// is ignored and will be automatically logged for --log-level debug
|
||||
if id, err := s.pullCache(ctx, cacheKey); id != "" && err == nil {
|
||||
logCachePulled(cacheKey)
|
||||
if ref, id, err := s.pullCache(ctx, cacheKey); ref != nil && id != "" && err == nil {
|
||||
logCachePulled(cacheKey, ref)
|
||||
cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("checking if cached image exists from a previous build: %w", err)
|
||||
@@ -1340,12 +1357,12 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// All the best effort to find cache on localstorage have failed try pulling
|
||||
// cache from remote repo if `--cache-from` was configured and cacheKey was
|
||||
// generated again after adding content summary.
|
||||
if cacheID == "" && s.executor.cacheFrom != nil {
|
||||
if cacheID == "" && len(s.executor.cacheFrom) != 0 {
|
||||
// only attempt to use cache again if pulling was successful
|
||||
// otherwise do nothing and attempt to run the step, err != nil
|
||||
// is ignored and will be automatically logged for --log-level debug
|
||||
if id, err := s.pullCache(ctx, cacheKey); id != "" && err == nil {
|
||||
logCachePulled(cacheKey)
|
||||
if ref, id, err := s.pullCache(ctx, cacheKey); ref != nil && id != "" && err == nil {
|
||||
logCachePulled(cacheKey, ref)
|
||||
cacheID, err = s.intermediateImageExists(ctx, node, addedContentSummary, s.stepRequiresLayer(step))
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("checking if cached image exists from a previous build: %w", err)
|
||||
@@ -1425,7 +1442,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// Try to push this cache to remote repository only
|
||||
// if cache was present on local storage and not
|
||||
// pulled from remote source while processing this
|
||||
if s.executor.cacheTo != nil && (!pulledAndUsedCacheImage || cacheID == "") {
|
||||
if len(s.executor.cacheTo) != 0 && (!pulledAndUsedCacheImage || cacheID == "") && needsCacheKey {
|
||||
logCachePush(cacheKey)
|
||||
if err = s.pushCache(ctx, imgID, cacheKey); err != nil {
|
||||
return "", nil, err
|
||||
@@ -1489,7 +1506,7 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||
// Enforce pull "never" since we already have an image
|
||||
// ID that we really should not be pulling anymore (see
|
||||
// containers/podman/issues/10307).
|
||||
if _, err := s.prepare(ctx, imgID, false, true, define.PullNever); err != nil {
|
||||
if _, err := s.prepare(ctx, imgID, false, true, true, define.PullNever); err != nil {
|
||||
return "", nil, fmt.Errorf("preparing container for next step: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -1816,10 +1833,10 @@ func (s *StageExecutor) pushCache(ctx context.Context, src, cacheKey string) err
|
||||
// or a newer version of cache was found in the upstream repo. If new
|
||||
// image was pulled function returns image id otherwise returns empty
|
||||
// string "" or error if any error was encontered while pulling the cache.
|
||||
func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (string, error) {
|
||||
func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (reference.Named, string, error) {
|
||||
srcList, err := cacheImageReferences(s.executor.cacheFrom, cacheKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, "", err
|
||||
}
|
||||
for _, src := range srcList {
|
||||
logrus.Debugf("trying to pull cache from remote repo: %+v", src.DockerReference())
|
||||
@@ -1841,9 +1858,9 @@ func (s *StageExecutor) pullCache(ctx context.Context, cacheKey string) (string,
|
||||
//return "", fmt.Errorf("failed while pulling cache from %q: %w", src, err)
|
||||
}
|
||||
logrus.Debugf("successfully pulled cache from repo %s: %s", src, id)
|
||||
return id, nil
|
||||
return src.DockerReference(), id, nil
|
||||
}
|
||||
return "", fmt.Errorf("failed pulling cache from all available sources %q", srcList)
|
||||
return nil, "", fmt.Errorf("failed pulling cache from all available sources %q", srcList)
|
||||
}
|
||||
|
||||
// intermediateImageExists returns true if an intermediate image of currNode exists in the image store from a previous build.
|
||||
@@ -2005,14 +2022,6 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
|
||||
if s.executor.commonBuildOptions.IdentityLabel == types.OptionalBoolUndefined || s.executor.commonBuildOptions.IdentityLabel == types.OptionalBoolTrue {
|
||||
s.builder.SetLabel(buildah.BuilderIdentityAnnotation, define.Version)
|
||||
}
|
||||
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 {
|
||||
@@ -2071,7 +2080,7 @@ func (s *StageExecutor) generateBuildOutput(buildOutputOpts define.BuildOutputOp
|
||||
// decided to strip setuid,setgid and extended attributes.
|
||||
// Since modes like setuid,setgid leaves room for executable
|
||||
// to get invoked with different file-system permission its safer
|
||||
// to strip them off for unpriviledged invocation.
|
||||
// to strip them off for unprivileged invocation.
|
||||
// See: https://github.com/containers/buildah/pull/3823#discussion_r829376633
|
||||
extractRootfsOpts.StripSetuidBit = true
|
||||
extractRootfsOpts.StripSetgidBit = true
|
||||
|
||||
+4
-8
@@ -252,16 +252,12 @@ The build steps for Buildah on SUSE / openSUSE are the same as for Fedora, above
|
||||
|
||||
### Ubuntu
|
||||
|
||||
In Ubuntu zesty and xenial, you can use these commands:
|
||||
In Ubuntu jammy you can use these commands:
|
||||
|
||||
```
|
||||
sudo apt-get -y install software-properties-common
|
||||
sudo add-apt-repository -y ppa:alexlarsson/flatpak
|
||||
sudo add-apt-repository -y ppa:gophers/archive
|
||||
sudo apt-add-repository -y ppa:projectatomic/ppa
|
||||
sudo apt-get -y -qq update
|
||||
sudo apt-get -y install bats btrfs-tools git libapparmor-dev libdevmapper-dev libglib2.0-dev libgpgme11-dev libseccomp-dev libselinux1-dev skopeo-containers go-md2man
|
||||
sudo apt-get -y install golang-1.13
|
||||
sudo apt-get -y install bats btrfs-progs git libapparmor-dev libdevmapper-dev libglib2.0-dev libgpgme11-dev libseccomp-dev libselinux1-dev skopeo go-md2man make
|
||||
sudo apt-get -y install golang-1.18
|
||||
```
|
||||
Then to install Buildah on Ubuntu follow the steps in this example:
|
||||
|
||||
@@ -271,7 +267,7 @@ Then to install Buildah on Ubuntu follow the steps in this example:
|
||||
export GOPATH=`pwd`
|
||||
git clone https://github.com/containers/buildah ./src/github.com/containers/buildah
|
||||
cd ./src/github.com/containers/buildah
|
||||
PATH=/usr/lib/go-1.13/bin:$PATH make runc all SECURITYTAGS="apparmor seccomp"
|
||||
PATH=/usr/lib/go-1.18/bin:$PATH make runc all SECURITYTAGS="apparmor seccomp"
|
||||
sudo make install install.runc
|
||||
buildah --help
|
||||
```
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ func ExportFromReader(input io.Reader, opts define.BuildOutputOption) error {
|
||||
// invoked as root since caller already has access to artifacts
|
||||
// therefore we can preserve ownership as is, however for rootless users
|
||||
// ownership has to be changed so exported artifacts can still
|
||||
// be accessible by unpriviledged users.
|
||||
// be accessible by unprivileged users.
|
||||
// See: https://github.com/containers/buildah/pull/3823#discussion_r829376633
|
||||
noLChown := false
|
||||
if unshare.IsRootless() {
|
||||
|
||||
+13
-10
@@ -286,15 +286,6 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
||||
namespaceOptions := defaultNamespaceOptions
|
||||
namespaceOptions.AddOrReplace(options.NamespaceOptions...)
|
||||
|
||||
// Set the base-image annotations as suggested by the OCI image spec.
|
||||
imageAnnotations := map[string]string{}
|
||||
imageAnnotations[v1.AnnotationBaseImageDigest] = imageDigest
|
||||
if !shortnames.IsShortName(imageSpec) {
|
||||
// If the base image could be resolved to a fully-qualified
|
||||
// image name, let's set it.
|
||||
imageAnnotations[v1.AnnotationBaseImageName] = imageSpec
|
||||
}
|
||||
|
||||
builder := &Builder{
|
||||
store: store,
|
||||
Type: containerType,
|
||||
@@ -304,7 +295,7 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
||||
GroupAdd: options.GroupAdd,
|
||||
Container: name,
|
||||
ContainerID: container.ID,
|
||||
ImageAnnotations: imageAnnotations,
|
||||
ImageAnnotations: map[string]string{},
|
||||
ImageCreatedBy: "",
|
||||
ProcessLabel: container.ProcessLabel(),
|
||||
MountLabel: container.MountLabel(),
|
||||
@@ -341,6 +332,18 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions
|
||||
if err := builder.initConfig(ctx, src, systemContext); err != nil {
|
||||
return nil, fmt.Errorf("preparing image configuration: %w", err)
|
||||
}
|
||||
|
||||
if !options.PreserveBaseImageAnns {
|
||||
builder.SetAnnotation(v1.AnnotationBaseImageDigest, imageDigest)
|
||||
if !shortnames.IsShortName(imageSpec) {
|
||||
// If the base image was specified as a fully-qualified
|
||||
// image name, let's set it.
|
||||
builder.SetAnnotation(v1.AnnotationBaseImageName, imageSpec)
|
||||
} else {
|
||||
builder.UnsetAnnotation(v1.AnnotationBaseImageName)
|
||||
}
|
||||
}
|
||||
|
||||
err = builder.Save()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("saving builder state for container %q: %w", builder.ContainerID, err)
|
||||
|
||||
+40
-13
@@ -105,19 +105,16 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
logrus.Debugf("Pull Policy for pull [%v]", pullPolicy)
|
||||
|
||||
args := make(map[string]string)
|
||||
if c.Flag("build-arg-file").Changed {
|
||||
for _, argfile := range iopts.BuildArgFile {
|
||||
if err := readBuildArgFile(argfile, args); err != nil {
|
||||
return options, nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Flag("build-arg").Changed {
|
||||
for _, arg := range iopts.BuildArg {
|
||||
av := strings.SplitN(arg, "=", 2)
|
||||
if len(av) > 1 {
|
||||
args[av[0]] = av[1]
|
||||
} else {
|
||||
// check if the env is set in the local environment and use that value if it is
|
||||
if val, present := os.LookupEnv(av[0]); present {
|
||||
args[av[0]] = val
|
||||
} else {
|
||||
delete(args, av[0])
|
||||
}
|
||||
}
|
||||
readBuildArg(arg, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +322,7 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
// If user explicitly specified `--cache-ttl=0s`
|
||||
// it would effectively mean that user is asking
|
||||
// to use no cache at all. In such use cases
|
||||
// buildah can skip looking for cache entierly
|
||||
// buildah can skip looking for cache entirely
|
||||
// by setting `--no-cache=true` internally.
|
||||
if int64(cacheTTL) == 0 {
|
||||
logrus.Debug("Setting --no-cache=true since --cache-ttl was set to 0s which effectively means user wants to ignore cache")
|
||||
@@ -375,7 +372,6 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
ContextDirectory: contextDir,
|
||||
Devices: iopts.Devices,
|
||||
DropCapabilities: iopts.CapDrop,
|
||||
Envs: iopts.Envs,
|
||||
Err: stderr,
|
||||
Excludes: excludes,
|
||||
ForceRmIntermediateCtrs: iopts.ForceRm,
|
||||
@@ -425,9 +421,40 @@ func GenBuildOptions(c *cobra.Command, inputArgs []string, iopts BuildOptions) (
|
||||
if iopts.Quiet {
|
||||
options.ReportWriter = io.Discard
|
||||
}
|
||||
|
||||
options.Envs = LookupEnvVarReferences(iopts.Envs, os.Environ())
|
||||
|
||||
return options, containerfiles, removeAll, nil
|
||||
}
|
||||
|
||||
func readBuildArgFile(buildargfile string, args map[string]string) error {
|
||||
argfile, err := os.ReadFile(buildargfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, arg := range strings.Split(string(argfile), "\n") {
|
||||
if len (arg) == 0 || arg[0] == '#' {
|
||||
continue
|
||||
}
|
||||
readBuildArg(arg, args)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func readBuildArg(buildarg string, args map[string]string) {
|
||||
av := strings.SplitN(buildarg, "=", 2)
|
||||
if len(av) > 1 {
|
||||
args[av[0]] = av[1]
|
||||
} else {
|
||||
// check if the env is set in the local environment and use that value if it is
|
||||
if val, present := os.LookupEnv(av[0]); present {
|
||||
args[av[0]] = val
|
||||
} else {
|
||||
delete(args, av[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getContainerfiles(files []string) []string {
|
||||
var containerfiles []string
|
||||
for _, f := range files {
|
||||
|
||||
+42
@@ -53,6 +53,7 @@ type BudResults struct {
|
||||
Annotation []string
|
||||
Authfile string
|
||||
BuildArg []string
|
||||
BuildArgFile []string
|
||||
BuildContext []string
|
||||
CacheFrom []string
|
||||
CacheTo []string
|
||||
@@ -204,6 +205,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
|
||||
fs.StringVar(&flags.Authfile, "authfile", "", "path of the authentication file.")
|
||||
fs.StringArrayVar(&flags.OCIHooksDir, "hooks-dir", []string{}, "set the OCI hooks directory path (may be set multiple times)")
|
||||
fs.StringArrayVar(&flags.BuildArg, "build-arg", []string{}, "`argument=value` to supply to the builder")
|
||||
fs.StringArrayVar(&flags.BuildArgFile, "build-arg-file", []string{}, "`argfile.conf` containing lines of argument=value to supply to the builder")
|
||||
fs.StringArrayVar(&flags.BuildContext, "build-context", []string{}, "`argument=value` to supply additional build context to the builder")
|
||||
fs.StringArrayVar(&flags.CacheFrom, "cache-from", []string{}, "remote repository list to utilise as potential cache source.")
|
||||
fs.StringArrayVar(&flags.CacheTo, "cache-to", []string{}, "remote repository list to utilise as potential cache destination.")
|
||||
@@ -285,6 +287,7 @@ func GetBudFlagsCompletions() commonComp.FlagCompletions {
|
||||
flagCompletion["arch"] = commonComp.AutocompleteNone
|
||||
flagCompletion["authfile"] = commonComp.AutocompleteDefault
|
||||
flagCompletion["build-arg"] = commonComp.AutocompleteNone
|
||||
flagCompletion["build-arg-file"] = commonComp.AutocompleteDefault
|
||||
flagCompletion["build-context"] = commonComp.AutocompleteNone
|
||||
flagCompletion["cache-from"] = commonComp.AutocompleteNone
|
||||
flagCompletion["cache-to"] = commonComp.AutocompleteNone
|
||||
@@ -481,3 +484,42 @@ func AliasFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
}
|
||||
|
||||
// LookupEnvVarReferences returns a copy of specs with keys and values resolved
|
||||
// from environ. Strings are in "key=value" form, the same as [os.Environ].
|
||||
//
|
||||
// - When a string in specs lacks "=", it is treated as a key and the value
|
||||
// is retrieved from environ. When the key is missing from environ, neither
|
||||
// the key nor value are returned.
|
||||
//
|
||||
// - When a string in specs lacks "=" and ends with "*", it is treated as
|
||||
// a key prefix and any keys with the same prefix in environ are returned.
|
||||
//
|
||||
// - When a string in specs is exactly "*", all keys and values in environ
|
||||
// are returned.
|
||||
func LookupEnvVarReferences(specs, environ []string) []string {
|
||||
result := make([]string, 0, len(specs))
|
||||
|
||||
for _, spec := range specs {
|
||||
if key, _, ok := strings.Cut(spec, "="); ok {
|
||||
result = append(result, spec)
|
||||
|
||||
} else if key == "*" {
|
||||
result = append(result, environ...)
|
||||
|
||||
} else {
|
||||
prefix := key + "="
|
||||
if strings.HasSuffix(key, "*") {
|
||||
prefix = strings.TrimSuffix(key, "*")
|
||||
}
|
||||
|
||||
for _, spec := range environ {
|
||||
if strings.HasPrefix(spec, prefix) {
|
||||
result = append(result, spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
+5
-1
@@ -164,7 +164,7 @@ func (a *AgentServer) ServePath() string {
|
||||
// readOnlyAgent implemetnts the agent.Agent interface
|
||||
// readOnlyAgent allows reads only to prevent keys from being added from the build to the forwarded ssh agent on the host
|
||||
type readOnlyAgent struct {
|
||||
agent.Agent
|
||||
agent.ExtendedAgent
|
||||
}
|
||||
|
||||
func (a *readOnlyAgent) Add(_ agent.AddedKey) error {
|
||||
@@ -183,6 +183,10 @@ func (a *readOnlyAgent) Lock(_ []byte) error {
|
||||
return errors.New("locking agent not allowed by buildah")
|
||||
}
|
||||
|
||||
func (a *readOnlyAgent) Extension(_ string, _ []byte) ([]byte, error) {
|
||||
return nil, errors.New("extensions not allowed by buildah")
|
||||
}
|
||||
|
||||
// Source is what the forwarded agent's source is
|
||||
// The source of the forwarded agent can be from a socket on the host, or from individual key files
|
||||
type Source struct {
|
||||
|
||||
+8
-1
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/containers/buildah/define"
|
||||
"github.com/containers/buildah/internal"
|
||||
"github.com/containers/buildah/pkg/jail"
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/buildah/util"
|
||||
"github.com/containers/common/libnetwork/resolvconf"
|
||||
nettypes "github.com/containers/common/libnetwork/types"
|
||||
@@ -98,7 +99,13 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
||||
if isolation == IsolationDefault {
|
||||
isolation = b.Isolation
|
||||
if isolation == IsolationDefault {
|
||||
isolation = IsolationOCI
|
||||
isolation, err = parse.IsolationOption("")
|
||||
if err != nil {
|
||||
logrus.Debugf("got %v while trying to determine default isolation, guessing OCI", err)
|
||||
isolation = IsolationOCI
|
||||
} else if isolation == IsolationDefault {
|
||||
isolation = IsolationOCI
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := checkAndOverrideIsolationOptions(isolation, &options); err != nil {
|
||||
|
||||
+9
-3
@@ -96,7 +96,13 @@ func (b *Builder) Run(command []string, options RunOptions) error {
|
||||
if isolation == define.IsolationDefault {
|
||||
isolation = b.Isolation
|
||||
if isolation == define.IsolationDefault {
|
||||
isolation = define.IsolationOCI
|
||||
isolation, err = parse.IsolationOption("")
|
||||
if err != nil {
|
||||
logrus.Debugf("got %v while trying to determine default isolation, guessing OCI", err)
|
||||
isolation = IsolationOCI
|
||||
} else if isolation == IsolationDefault {
|
||||
isolation = IsolationOCI
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := checkAndOverrideIsolationOptions(isolation, &options); err != nil {
|
||||
@@ -1098,8 +1104,8 @@ func setupSpecialMountSpecChanges(spec *spec.Spec, shmSize string) ([]specs.Moun
|
||||
}
|
||||
|
||||
addCgroup := true
|
||||
// mount sys when root and no userns or when both netns and userns are private
|
||||
canMountSys := (!isRootless && !isNewUserns) || (isNetns && isNewUserns)
|
||||
// mount sys when root and no userns or when a new netns is created
|
||||
canMountSys := (!isRootless && !isNewUserns) || isNetns
|
||||
if !canMountSys {
|
||||
addCgroup = false
|
||||
sys := "/sys"
|
||||
|
||||
+97
-11
@@ -203,13 +203,17 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
|
||||
return nil, fmt.Errorf("unsupported bridge network option %s", key)
|
||||
}
|
||||
}
|
||||
case types.MacVLANNetworkDriver:
|
||||
err = createMacvlan(newNetwork)
|
||||
case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver:
|
||||
err = createIpvlanOrMacvlan(newNetwork)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported driver %s: %w", newNetwork.Driver, types.ErrInvalidArg)
|
||||
net, err := n.createPlugin(newNetwork)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newNetwork = net
|
||||
}
|
||||
|
||||
// when we do not have ipam we must disable dns
|
||||
@@ -217,12 +221,12 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
|
||||
|
||||
// process NetworkDNSServers
|
||||
if len(newNetwork.NetworkDNSServers) > 0 && !newNetwork.DNSEnabled {
|
||||
return nil, fmt.Errorf("Cannot set NetworkDNSServers if DNS is not enabled for the network: %w", types.ErrInvalidArg)
|
||||
return nil, fmt.Errorf("cannot set NetworkDNSServers if DNS is not enabled for the network: %w", types.ErrInvalidArg)
|
||||
}
|
||||
// validate ip address
|
||||
for _, dnsServer := range newNetwork.NetworkDNSServers {
|
||||
if net.ParseIP(dnsServer) == nil {
|
||||
return nil, fmt.Errorf("Unable to parse ip %s specified in NetworkDNSServers: %w", dnsServer, types.ErrInvalidArg)
|
||||
return nil, fmt.Errorf("unable to parse ip %s specified in NetworkDNSServers: %w", dnsServer, types.ErrInvalidArg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +249,10 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
|
||||
return newNetwork, nil
|
||||
}
|
||||
|
||||
func createMacvlan(network *types.Network) error {
|
||||
// ipvlan shares the same mac address so supporting DHCP is not really possible
|
||||
var errIpvlanNoDHCP = errors.New("ipam driver dhcp is not supported with ipvlan")
|
||||
|
||||
func createIpvlanOrMacvlan(network *types.Network) error {
|
||||
if network.NetworkInterface != "" {
|
||||
interfaceNames, err := internalutil.GetLiveNetworkNames()
|
||||
if err != nil {
|
||||
@@ -256,6 +263,12 @@ func createMacvlan(network *types.Network) error {
|
||||
}
|
||||
}
|
||||
|
||||
driver := network.Driver
|
||||
isMacVlan := true
|
||||
if driver == types.IPVLANNetworkDriver {
|
||||
isMacVlan = false
|
||||
}
|
||||
|
||||
// always turn dns off with macvlan, it is not implemented in netavark
|
||||
// and makes little sense to support with macvlan
|
||||
// see https://github.com/containers/netavark/pull/467
|
||||
@@ -264,10 +277,25 @@ func createMacvlan(network *types.Network) error {
|
||||
// we already validated the drivers before so we just have to set the default here
|
||||
switch network.IPAMOptions[types.Driver] {
|
||||
case "":
|
||||
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
|
||||
if len(network.Subnets) == 0 {
|
||||
// if no subnets and no driver choose dhcp
|
||||
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
|
||||
if !isMacVlan {
|
||||
return errIpvlanNoDHCP
|
||||
}
|
||||
} else {
|
||||
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
|
||||
}
|
||||
case types.HostLocalIPAMDriver:
|
||||
if len(network.Subnets) == 0 {
|
||||
return fmt.Errorf("macvlan driver needs at least one subnet specified, when the host-local ipam driver is set")
|
||||
return fmt.Errorf("%s driver needs at least one subnet specified when the host-local ipam driver is set", driver)
|
||||
}
|
||||
case types.DHCPIPAMDriver:
|
||||
if !isMacVlan {
|
||||
return errIpvlanNoDHCP
|
||||
}
|
||||
if len(network.Subnets) > 0 {
|
||||
return fmt.Errorf("ipam driver dhcp set but subnets are set")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,8 +303,14 @@ func createMacvlan(network *types.Network) error {
|
||||
for key, value := range network.Options {
|
||||
switch key {
|
||||
case types.ModeOption:
|
||||
if !util.StringInSlice(value, types.ValidMacVLANModes) {
|
||||
return fmt.Errorf("unknown macvlan mode %q", value)
|
||||
if isMacVlan {
|
||||
if !util.StringInSlice(value, types.ValidMacVLANModes) {
|
||||
return fmt.Errorf("unknown macvlan mode %q", value)
|
||||
}
|
||||
} else {
|
||||
if !util.StringInSlice(value, types.ValidIPVLANModes) {
|
||||
return fmt.Errorf("unknown ipvlan mode %q", value)
|
||||
}
|
||||
}
|
||||
case types.MTUOption:
|
||||
_, err := internalutil.ParseMTU(value)
|
||||
@@ -284,7 +318,7 @@ func createMacvlan(network *types.Network) error {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported macvlan network option %s", key)
|
||||
return fmt.Errorf("unsupported %s network option %s", driver, key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -373,3 +407,55 @@ func validateIPAMDriver(n *types.Network) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errInvalidPluginResult = errors.New("invalid plugin result")
|
||||
|
||||
func (n *netavarkNetwork) createPlugin(net *types.Network) (*types.Network, error) {
|
||||
path, err := getPlugin(net.Driver, n.pluginDirs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := new(types.Network)
|
||||
err = n.execPlugin(path, []string{"create"}, net, result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin %s failed: %w", path, err)
|
||||
}
|
||||
// now make sure that neither the name, ID, driver were changed by the plugin
|
||||
if net.Name != result.Name {
|
||||
return nil, fmt.Errorf("%w: changed network name", errInvalidPluginResult)
|
||||
}
|
||||
if net.ID != result.ID {
|
||||
return nil, fmt.Errorf("%w: changed network ID", errInvalidPluginResult)
|
||||
}
|
||||
if net.Driver != result.Driver {
|
||||
return nil, fmt.Errorf("%w: changed network driver", errInvalidPluginResult)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getAllPlugins(dirs []string) []string {
|
||||
var plugins []string
|
||||
for _, dir := range dirs {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err == nil {
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if !util.StringInSlice(name, plugins) {
|
||||
plugins = append(plugins, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return plugins
|
||||
}
|
||||
|
||||
func getPlugin(name string, dirs []string) (string, error) {
|
||||
for _, dir := range dirs {
|
||||
fullpath := filepath.Join(dir, name)
|
||||
st, err := os.Stat(fullpath)
|
||||
if err == nil && st.Mode().IsRegular() {
|
||||
return fullpath, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("failed to find driver or plugin %q", name)
|
||||
}
|
||||
+20
-11
@@ -76,7 +76,24 @@ func getRustLogEnv() string {
|
||||
// used to marshal the netavark output into it. This can be nil.
|
||||
// All errors return by this function should be of the type netavarkError
|
||||
// to provide a helpful error message.
|
||||
func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{}) error {
|
||||
func (n *netavarkNetwork) execNetavark(args []string, needPlugin bool, stdin, result interface{}) error {
|
||||
// set the netavark log level to the same as the podman
|
||||
env := append(os.Environ(), getRustLogEnv())
|
||||
// if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
env = append(env, "RUST_BACKTRACE=1")
|
||||
}
|
||||
if n.dnsBindPort != 0 {
|
||||
env = append(env, "NETAVARK_DNS_PORT="+strconv.Itoa(int(n.dnsBindPort)))
|
||||
}
|
||||
return n.execBinary(n.netavarkBinary, append(n.getCommonNetavarkOptions(needPlugin), args...), stdin, result, env)
|
||||
}
|
||||
|
||||
func (n *netavarkNetwork) execPlugin(path string, args []string, stdin, result interface{}) error {
|
||||
return n.execBinary(path, args, stdin, result, nil)
|
||||
}
|
||||
|
||||
func (n *netavarkNetwork) execBinary(path string, args []string, stdin, result interface{}, env []string) error {
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
return newNetavarkError("failed to create stdin pipe", err)
|
||||
@@ -108,20 +125,12 @@ func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{})
|
||||
logWriter = io.MultiWriter(logWriter, &logrusNetavarkWriter{})
|
||||
}
|
||||
|
||||
cmd := exec.Command(n.netavarkBinary, append(n.getCommonNetavarkOptions(), args...)...)
|
||||
cmd := exec.Command(path, args...)
|
||||
// connect the pipes to stdin and stdout
|
||||
cmd.Stdin = stdinR
|
||||
cmd.Stdout = stdoutW
|
||||
cmd.Stderr = logWriter
|
||||
// set the netavark log level to the same as the podman
|
||||
cmd.Env = append(os.Environ(), getRustLogEnv())
|
||||
// if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
cmd.Env = append(cmd.Env, "RUST_BACKTRACE=1")
|
||||
}
|
||||
if n.dnsBindPort != 0 {
|
||||
cmd.Env = append(cmd.Env, "NETAVARK_DNS_PORT="+strconv.Itoa(int(n.dnsBindPort)))
|
||||
}
|
||||
cmd.Env = env
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
|
||||
+11
-1
@@ -46,6 +46,9 @@ type netavarkNetwork struct {
|
||||
// dnsBindPort is set the the port to pass to netavark for aardvark
|
||||
dnsBindPort uint16
|
||||
|
||||
// pluginDirs list of directories were netavark plugins are located
|
||||
pluginDirs []string
|
||||
|
||||
// ipamDBPath is the path to the ip allocation bolt db
|
||||
ipamDBPath string
|
||||
|
||||
@@ -86,6 +89,9 @@ type InitConfig struct {
|
||||
// DNSBindPort is set the the port to pass to netavark for aardvark
|
||||
DNSBindPort uint16
|
||||
|
||||
// PluginDirs list of directories were netavark plugins are located
|
||||
PluginDirs []string
|
||||
|
||||
// Syslog describes whenever the netavark debbug output should be log to the syslog as well.
|
||||
// This will use logrus to do so, make sure logrus is set up to log to the syslog.
|
||||
Syslog bool
|
||||
@@ -143,6 +149,7 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) {
|
||||
defaultSubnet: defaultNet,
|
||||
defaultsubnetPools: defaultSubnetPools,
|
||||
dnsBindPort: conf.DNSBindPort,
|
||||
pluginDirs: conf.PluginDirs,
|
||||
lock: lock,
|
||||
syslog: conf.Syslog,
|
||||
}
|
||||
@@ -150,10 +157,13 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
var builtinDrivers = []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver}
|
||||
|
||||
// Drivers will return the list of supported network drivers
|
||||
// for this interface.
|
||||
func (n *netavarkNetwork) Drivers() []string {
|
||||
return []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver}
|
||||
paths := getAllPlugins(n.pluginDirs)
|
||||
return append(builtinDrivers, paths...)
|
||||
}
|
||||
|
||||
// DefaultNetworkName will return the default netavark network name.
|
||||
|
||||
+24
-10
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/containers/common/libnetwork/internal/util"
|
||||
"github.com/containers/common/libnetwork/types"
|
||||
pkgutil "github.com/containers/common/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -20,7 +21,7 @@ type netavarkOptions struct {
|
||||
}
|
||||
|
||||
func (n *netavarkNetwork) execUpdate(networkName string, networkDNSServers []string) error {
|
||||
retErr := n.execNetavark([]string{"update", networkName, "--network-dns-servers", strings.Join(networkDNSServers, ",")}, nil, nil)
|
||||
retErr := n.execNetavark([]string{"update", networkName, "--network-dns-servers", strings.Join(networkDNSServers, ",")}, false, nil, nil)
|
||||
return retErr
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ func (n *netavarkNetwork) Setup(namespacePath string, options types.SetupOptions
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netavarkOpts, err := n.convertNetOpts(options.NetworkOptions)
|
||||
netavarkOpts, needPlugin, err := n.convertNetOpts(options.NetworkOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert net opts: %w", err)
|
||||
}
|
||||
@@ -71,7 +72,7 @@ func (n *netavarkNetwork) Setup(namespacePath string, options types.SetupOptions
|
||||
}
|
||||
|
||||
result := map[string]types.StatusBlock{}
|
||||
err = n.execNetavark([]string{"setup", namespacePath}, netavarkOpts, &result)
|
||||
err = n.execNetavark([]string{"setup", namespacePath}, needPlugin, netavarkOpts, &result)
|
||||
if err != nil {
|
||||
// lets dealloc ips to prevent leaking
|
||||
if err := n.deallocIPs(&options.NetworkOptions); err != nil {
|
||||
@@ -106,12 +107,12 @@ func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownO
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
netavarkOpts, err := n.convertNetOpts(options.NetworkOptions)
|
||||
netavarkOpts, needPlugin, err := n.convertNetOpts(options.NetworkOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert net opts: %w", err)
|
||||
}
|
||||
|
||||
retErr := n.execNetavark([]string{"teardown", namespacePath}, netavarkOpts, nil)
|
||||
retErr := n.execNetavark([]string{"teardown", namespacePath}, needPlugin, netavarkOpts, nil)
|
||||
|
||||
// when netavark returned an error we still free the used ips
|
||||
// otherwise we could end up in a state where block the ips forever
|
||||
@@ -127,22 +128,35 @@ func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownO
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (n *netavarkNetwork) getCommonNetavarkOptions() []string {
|
||||
return []string{"--config", n.networkRunDir, "--rootless=" + strconv.FormatBool(n.networkRootless), "--aardvark-binary=" + n.aardvarkBinary}
|
||||
func (n *netavarkNetwork) getCommonNetavarkOptions(needPlugin bool) []string {
|
||||
opts := []string{"--config", n.networkRunDir, "--rootless=" + strconv.FormatBool(n.networkRootless), "--aardvark-binary=" + n.aardvarkBinary}
|
||||
// to allow better backwards compat we only add the new netavark option when really needed
|
||||
if needPlugin {
|
||||
// Note this will require a netavark with https://github.com/containers/netavark/pull/509
|
||||
for _, dir := range n.pluginDirs {
|
||||
opts = append(opts, "--plugin-directory", dir)
|
||||
}
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOptions, error) {
|
||||
func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOptions, bool, error) {
|
||||
netavarkOptions := netavarkOptions{
|
||||
NetworkOptions: opts,
|
||||
Networks: make(map[string]*types.Network, len(opts.Networks)),
|
||||
}
|
||||
|
||||
needsPlugin := false
|
||||
|
||||
for network := range opts.Networks {
|
||||
net, err := n.getNetwork(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
netavarkOptions.Networks[network] = net
|
||||
if !pkgutil.StringInSlice(net.Driver, builtinDrivers) {
|
||||
needsPlugin = true
|
||||
}
|
||||
}
|
||||
return &netavarkOptions, nil
|
||||
return &netavarkOptions, needsPlugin, nil
|
||||
}
|
||||
+1
@@ -81,6 +81,7 @@ func NetworkBackend(store storage.Store, conf *config.Config, syslog bool) (type
|
||||
NetworkRunDir: runDir,
|
||||
NetavarkBinary: netavarkBin,
|
||||
AardvarkBinary: aardvarkBin,
|
||||
PluginDirs: conf.Network.NetavarkPluginDirs,
|
||||
DefaultNetwork: conf.Network.DefaultNetwork,
|
||||
DefaultSubnet: conf.Network.DefaultSubnet,
|
||||
DefaultsubnetPools: conf.Network.DefaultSubnetPools,
|
||||
|
||||
+4
-4
@@ -94,7 +94,7 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if !opts.AcceptUnspecifiedRegistry {
|
||||
return errors.New("please provide a registry to login to")
|
||||
return errors.New("please provide a registry to log in to")
|
||||
}
|
||||
if key, err = defaultRegistryWhenUnspecified(systemContext); err != nil {
|
||||
return err
|
||||
@@ -109,7 +109,7 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
|
||||
}
|
||||
|
||||
default:
|
||||
return errors.New("login accepts only one registry to login to")
|
||||
return errors.New("login accepts only one registry to log in to")
|
||||
}
|
||||
|
||||
authConfig, err := config.GetCredentials(systemContext, key)
|
||||
@@ -299,7 +299,7 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
|
||||
switch len(args) {
|
||||
case 0:
|
||||
if !opts.AcceptUnspecifiedRegistry {
|
||||
return errors.New("please provide a registry to logout from")
|
||||
return errors.New("please provide a registry to log out from")
|
||||
}
|
||||
if key, err = defaultRegistryWhenUnspecified(systemContext); err != nil {
|
||||
return err
|
||||
@@ -314,7 +314,7 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
|
||||
}
|
||||
|
||||
default:
|
||||
return errors.New("logout accepts only one registry to logout from")
|
||||
return errors.New("logout accepts only one registry to log out from")
|
||||
}
|
||||
|
||||
err = config.RemoveAuthentication(systemContext, key)
|
||||
|
||||
+1
-1
@@ -428,7 +428,7 @@ func (c *CgroupControl) CreateSystemdUnit(path string) error {
|
||||
return systemdCreate(path, conn)
|
||||
}
|
||||
|
||||
// GetUserConnection returns an user connection to D-BUS
|
||||
// GetUserConnection returns a user connection to D-BUS
|
||||
func GetUserConnection(uid int) (*systemdDbus.Conn, error) {
|
||||
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
|
||||
|
||||
+17
-4
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/containers/common/libnetwork/types"
|
||||
"github.com/containers/common/pkg/capabilities"
|
||||
"github.com/containers/common/pkg/util"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
units "github.com/docker/go-units"
|
||||
selinux "github.com/opencontainers/selinux/go-selinux"
|
||||
@@ -323,7 +324,7 @@ type EngineConfig struct {
|
||||
// Building/committing defaults to OCI.
|
||||
ImageDefaultFormat string `toml:"image_default_format,omitempty"`
|
||||
|
||||
// ImageVolumeMode Tells container engines how to handle the builtin
|
||||
// ImageVolumeMode Tells container engines how to handle the built-in
|
||||
// image volumes. Acceptable values are "bind", "tmpfs", and "ignore".
|
||||
ImageVolumeMode string `toml:"image_volume_mode,omitempty"`
|
||||
|
||||
@@ -337,6 +338,10 @@ type EngineConfig struct {
|
||||
// InitPath is the path to the container-init binary.
|
||||
InitPath string `toml:"init_path,omitempty"`
|
||||
|
||||
// KubeGenerateType sets the Kubernetes kind/specification to generate by default
|
||||
// with the podman kube generate command
|
||||
KubeGenerateType string `toml:"kube_generate_type,omitempty"`
|
||||
|
||||
// LockType is the type of locking to use.
|
||||
LockType string `toml:"lock_type,omitempty"`
|
||||
|
||||
@@ -553,6 +558,9 @@ type NetworkConfig struct {
|
||||
// CNIPluginDirs is where CNI plugin binaries are stored.
|
||||
CNIPluginDirs []string `toml:"cni_plugin_dirs,omitempty"`
|
||||
|
||||
// NetavarkPluginDirs is a list of directories which contain netavark plugins.
|
||||
NetavarkPluginDirs []string `toml:"netavark_plugin_dirs,omitempty"`
|
||||
|
||||
// DefaultNetwork is the network name of the default network
|
||||
// to attach pods to.
|
||||
DefaultNetwork string `toml:"default_network,omitempty"`
|
||||
@@ -836,7 +844,7 @@ func (c *Config) CheckCgroupsAndAdjustConfig() {
|
||||
|
||||
if !hasSession && unshare.GetRootlessUID() != 0 {
|
||||
logrus.Warningf("The cgroupv2 manager is set to systemd but there is no systemd user session available")
|
||||
logrus.Warningf("For using systemd, you may need to login using an user session")
|
||||
logrus.Warningf("For using systemd, you may need to log in using a user session")
|
||||
logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibly as root)", unshare.GetRootlessUID())
|
||||
logrus.Warningf("Falling back to --cgroup-manager=cgroupfs")
|
||||
c.Engine.CgroupManager = CgroupfsCgroupsManager
|
||||
@@ -1255,16 +1263,21 @@ func (c *Config) Write() error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
configFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o644)
|
||||
|
||||
opts := &ioutils.AtomicFileWriterOptions{ExplicitCommit: true}
|
||||
configFile, err := ioutils.NewAtomicFileWriterWithOpts(path, 0o644, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
enc := toml.NewEncoder(configFile)
|
||||
if err := enc.Encode(c); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
// If no errors commit the changes to the config file
|
||||
return configFile.Commit()
|
||||
}
|
||||
|
||||
// Reload clean the cached config and reloads the configuration from containers.conf files
|
||||
|
||||
+17
-4
@@ -304,6 +304,15 @@ default_sysctls = [
|
||||
# "/opt/cni/bin",
|
||||
#]
|
||||
|
||||
# List of directories that will be searched for netavark plugins.
|
||||
#
|
||||
#netavark_plugin_dirs = [
|
||||
# "/usr/local/libexec/netavark",
|
||||
# "/usr/libexec/netavark",
|
||||
# "/usr/local/lib/netavark",
|
||||
# "/usr/lib/netavark",
|
||||
#]
|
||||
|
||||
# The network name of the default network to attach pods to.
|
||||
#
|
||||
#default_network = "podman"
|
||||
@@ -457,7 +466,7 @@ default_sysctls = [
|
||||
#
|
||||
#image_parallel_copies = 0
|
||||
|
||||
# Tells container engines how to handle the builtin image volumes.
|
||||
# Tells container engines how to handle the built-in image volumes.
|
||||
# * bind: An anonymous named volume will be created and mounted
|
||||
# into the container.
|
||||
# * tmpfs: The volume is mounted onto the container as a tmpfs,
|
||||
@@ -473,13 +482,17 @@ default_sysctls = [
|
||||
|
||||
# Infra (pause) container image name for pod infra containers. When running a
|
||||
# pod, we start a `pause` process in a container to hold open the namespaces
|
||||
# associated with the pod. This container does nothing other then sleep,
|
||||
# reserving the pods resources for the lifetime of the pod. By default container
|
||||
# engines run a builtin container using the pause executable. If you want override
|
||||
# associated with the pod. This container does nothing other than sleep,
|
||||
# reserving the pod's resources for the lifetime of the pod. By default container
|
||||
# engines run a built-in container using the pause executable. If you want override
|
||||
# specify an image to pull.
|
||||
#
|
||||
#infra_image = ""
|
||||
|
||||
# Default Kubernetes kind/specification of the kubernetes yaml generated with the `podman kube generate` command.
|
||||
# The possible options are `pod` and `deployment`.
|
||||
#kube_generate_type = "pod"
|
||||
|
||||
# Specify the locking mechanism to use; valid values are "shm" and "file".
|
||||
# Change the default only if you are sure of what you are doing, in general
|
||||
# "file" is useful only on platforms where cgo is not available for using the
|
||||
|
||||
+12
-3
@@ -254,6 +254,15 @@ default_sysctls = [
|
||||
# "/opt/cni/bin",
|
||||
#]
|
||||
|
||||
# List of directories that will be searched for netavark plugins.
|
||||
#
|
||||
#netavark_plugin_dirs = [
|
||||
# "/usr/local/libexec/netavark",
|
||||
# "/usr/libexec/netavark",
|
||||
# "/usr/local/lib/netavark",
|
||||
# "/usr/lib/netavark",
|
||||
#]
|
||||
|
||||
# The network name of the default network to attach pods to.
|
||||
#
|
||||
#default_network = "podman"
|
||||
@@ -396,9 +405,9 @@ default_sysctls = [
|
||||
|
||||
# Infra (pause) container image name for pod infra containers. When running a
|
||||
# pod, we start a `pause` process in a container to hold open the namespaces
|
||||
# associated with the pod. This container does nothing other then sleep,
|
||||
# reserving the pods resources for the lifetime of the pod. By default container
|
||||
# engines run a builtin container using the pause executable. If you want override
|
||||
# associated with the pod. This container does nothing other than sleep,
|
||||
# reserving the pod's resources for the lifetime of the pod. By default container
|
||||
# engines run a built-in container using the pause executable. If you want override
|
||||
# specify an image to pull.
|
||||
#
|
||||
#infra_image = ""
|
||||
|
||||
+8
@@ -71,6 +71,12 @@ var (
|
||||
"/usr/lib/cni",
|
||||
"/opt/cni/bin",
|
||||
}
|
||||
DefaultNetavarkPluginDirs = []string{
|
||||
"/usr/local/libexec/netavark",
|
||||
"/usr/libexec/netavark",
|
||||
"/usr/local/lib/netavark",
|
||||
"/usr/lib/netavark",
|
||||
}
|
||||
DefaultSubnetPools = []SubnetPool{
|
||||
// 10.89.0.0/24-10.255.255.0/24
|
||||
parseSubnetPool("10.89.0.0/16", 24),
|
||||
@@ -214,6 +220,7 @@ func DefaultConfig() (*Config, error) {
|
||||
DefaultSubnetPools: DefaultSubnetPools,
|
||||
DNSBindPort: 0,
|
||||
CNIPluginDirs: DefaultCNIPluginDirs,
|
||||
NetavarkPluginDirs: DefaultNetavarkPluginDirs,
|
||||
},
|
||||
Engine: *defaultEngineConfig,
|
||||
Secrets: defaultSecretConfig(),
|
||||
@@ -418,6 +425,7 @@ func defaultConfigFromMemory() (*EngineConfig, error) {
|
||||
|
||||
c.PodExitPolicy = defaultPodExitPolicy
|
||||
c.SSHConfig = getDefaultSSHConfig()
|
||||
c.KubeGenerateType = "pod"
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package version
|
||||
|
||||
// Version is the version of the build.
|
||||
const Version = "0.52.0-dev"
|
||||
const Version = "0.52.0"
|
||||
+2
-2
@@ -176,7 +176,7 @@ func (ic *imageCopier) bpcRecompressCompressed(stream *sourceStream, detected bp
|
||||
recompressed, annotations := ic.compressedStream(decompressed, *ic.compressionFormat)
|
||||
// Note: recompressed must be closed on all return paths.
|
||||
stream.reader = recompressed
|
||||
stream.info = types.BlobInfo{ // FIXME? Should we preserve more data in src.info?
|
||||
stream.info = types.BlobInfo{ // FIXME? Should we preserve more data in src.info? Notably the current approach correctly removes zstd:chunked metadata annotations.
|
||||
Digest: "",
|
||||
Size: -1,
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (ic *imageCopier) bpcDecompressCompressed(stream *sourceStream, detected bp
|
||||
}
|
||||
// Note: s must be closed on all return paths.
|
||||
stream.reader = s
|
||||
stream.info = types.BlobInfo{ // FIXME? Should we preserve more data in src.info?
|
||||
stream.info = types.BlobInfo{ // FIXME? Should we preserve more data in src.info? Notably the current approach correctly removes zstd:chunked metadata annotations.
|
||||
Digest: "",
|
||||
Size: -1,
|
||||
}
|
||||
|
||||
+3
-3
@@ -739,9 +739,9 @@ func updatedBlobInfoFromReuse(inputInfo types.BlobInfo, reusedBlob private.Reuse
|
||||
res := types.BlobInfo{
|
||||
Digest: reusedBlob.Digest,
|
||||
Size: reusedBlob.Size,
|
||||
URLs: nil, // This _must_ be cleared if Digest changes; clear it in other cases as well, to preserve previous behavior.
|
||||
Annotations: inputInfo.Annotations,
|
||||
MediaType: inputInfo.MediaType, // Mostly irrelevant, MediaType is updated based on Compression*/CryptoOperation.
|
||||
URLs: nil, // This _must_ be cleared if Digest changes; clear it in other cases as well, to preserve previous behavior.
|
||||
Annotations: inputInfo.Annotations, // FIXME: This should remove zstd:chunked annotations (but those annotations being left with incorrect values should not break pulls)
|
||||
MediaType: inputInfo.MediaType, // Mostly irrelevant, MediaType is updated based on Compression*/CryptoOperation.
|
||||
CompressionOperation: reusedBlob.CompressionOperation,
|
||||
CompressionAlgorithm: reusedBlob.CompressionAlgorithm,
|
||||
CryptoOperation: inputInfo.CryptoOperation, // Expected to be unset anyway.
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest
|
||||
case types.Compress:
|
||||
info.MediaType = v1.MediaTypeImageLayerGzip
|
||||
info.CompressionAlgorithm = &compression.Gzip
|
||||
case types.Decompress:
|
||||
case types.Decompress: // FIXME: This should remove zstd:chunked annotations (but those annotations being left with incorrect values should not break pulls)
|
||||
info.MediaType = v1.MediaTypeImageLayer
|
||||
info.CompressionAlgorithm = nil
|
||||
}
|
||||
|
||||
+3
-3
@@ -44,21 +44,21 @@ func (c Algorithm) InternalUnstableUndocumentedMIMEQuestionMark() string {
|
||||
}
|
||||
|
||||
// AlgorithmCompressor returns the compressor field of algo.
|
||||
// This is a function instead of a public method so that it is only callable from by code
|
||||
// This is a function instead of a public method so that it is only callable by code
|
||||
// that is allowed to import this internal subpackage.
|
||||
func AlgorithmCompressor(algo Algorithm) CompressorFunc {
|
||||
return algo.compressor
|
||||
}
|
||||
|
||||
// AlgorithmDecompressor returns the decompressor field of algo.
|
||||
// This is a function instead of a public method so that it is only callable from by code
|
||||
// This is a function instead of a public method so that it is only callable by code
|
||||
// that is allowed to import this internal subpackage.
|
||||
func AlgorithmDecompressor(algo Algorithm) DecompressorFunc {
|
||||
return algo.decompressor
|
||||
}
|
||||
|
||||
// AlgorithmPrefix returns the prefix field of algo.
|
||||
// This is a function instead of a public method so that it is only callable from by code
|
||||
// This is a function instead of a public method so that it is only callable by code
|
||||
// that is allowed to import this internal subpackage.
|
||||
func AlgorithmPrefix(algo Algorithm) []byte {
|
||||
return algo.prefix
|
||||
|
||||
+17
-5
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/signature/internal"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// SignOptions includes optional parameters for signing container images.
|
||||
@@ -50,15 +51,26 @@ func SignDockerManifest(m []byte, dockerReference string, mech SigningMechanism,
|
||||
// using mech.
|
||||
func VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest []byte,
|
||||
expectedDockerReference string, mech SigningMechanism, expectedKeyIdentity string) (*Signature, error) {
|
||||
sig, _, err := VerifyImageManifestSignatureUsingKeyIdentityList(unverifiedSignature, unverifiedManifest, expectedDockerReference, mech, []string{expectedKeyIdentity})
|
||||
return sig, err
|
||||
}
|
||||
|
||||
// VerifyImageManifestSignatureUsingKeyIdentityList checks that unverifiedSignature uses one of the expectedKeyIdentities
|
||||
// to sign unverifiedManifest as expectedDockerReference, using mech. Returns the verified signature and the key identity that
|
||||
// was used to verify it.
|
||||
func VerifyImageManifestSignatureUsingKeyIdentityList(unverifiedSignature, unverifiedManifest []byte,
|
||||
expectedDockerReference string, mech SigningMechanism, expectedKeyIdentities []string) (*Signature, string, error) {
|
||||
expectedRef, err := reference.ParseNormalizedNamed(expectedDockerReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
var matchedKeyIdentity string
|
||||
sig, err := verifyAndExtractSignature(mech, unverifiedSignature, signatureAcceptanceRules{
|
||||
validateKeyIdentity: func(keyIdentity string) error {
|
||||
if keyIdentity != expectedKeyIdentity {
|
||||
return internal.NewInvalidSignatureError(fmt.Sprintf("Signature by %s does not match expected fingerprint %s", keyIdentity, expectedKeyIdentity))
|
||||
if !slices.Contains(expectedKeyIdentities, keyIdentity) {
|
||||
return internal.NewInvalidSignatureError(fmt.Sprintf("Signature by %s does not match expected fingerprints %v", keyIdentity, expectedKeyIdentities))
|
||||
}
|
||||
matchedKeyIdentity = keyIdentity
|
||||
return nil
|
||||
},
|
||||
validateSignedDockerReference: func(signedDockerReference string) error {
|
||||
@@ -84,7 +96,7 @@ func VerifyDockerManifestSignature(unverifiedSignature, unverifiedManifest []byt
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
return sig, nil
|
||||
return sig, matchedKeyIdentity, err
|
||||
}
|
||||
+55
-18
@@ -33,6 +33,58 @@ func (f *fulcioTrustRoot) validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fulcioIssuerInCertificate returns the OIDC issuer recorded by Fulcio in unutrustedCertificate;
|
||||
// it fails if the extension is not present in the certificate, or on any inconsistency.
|
||||
func fulcioIssuerInCertificate(untrustedCertificate *x509.Certificate) (string, error) {
|
||||
// == Validate the recorded OIDC issuer
|
||||
gotOIDCIssuer1 := false
|
||||
gotOIDCIssuer2 := false
|
||||
var oidcIssuer1, oidcIssuer2 string
|
||||
// certificate.ParseExtensions doesn’t reject duplicate extensions, and doesn’t detect inconsistencies
|
||||
// between certificate.OIDIssuer and certificate.OIDIssuerV2.
|
||||
// Go 1.19 rejects duplicate extensions universally; but until we can require Go 1.19,
|
||||
// reject duplicates manually.
|
||||
for _, untrustedExt := range untrustedCertificate.Extensions {
|
||||
if untrustedExt.Id.Equal(certificate.OIDIssuer) { //nolint:staticcheck // This is deprecated, but we must continue to accept it.
|
||||
if gotOIDCIssuer1 {
|
||||
// Coverage: This is unreachable in Go ≥1.19, which rejects certificates with duplicate extensions
|
||||
// already in ParseCertificate.
|
||||
return "", internal.NewInvalidSignatureError("Fulcio certificate has a duplicate OIDC issuer v1 extension")
|
||||
}
|
||||
oidcIssuer1 = string(untrustedExt.Value)
|
||||
gotOIDCIssuer1 = true
|
||||
} else if untrustedExt.Id.Equal(certificate.OIDIssuerV2) {
|
||||
if gotOIDCIssuer2 {
|
||||
// Coverage: This is unreachable in Go ≥1.19, which rejects certificates with duplicate extensions
|
||||
// already in ParseCertificate.
|
||||
return "", internal.NewInvalidSignatureError("Fulcio certificate has a duplicate OIDC issuer v2 extension")
|
||||
}
|
||||
rest, err := asn1.Unmarshal(untrustedExt.Value, &oidcIssuer2)
|
||||
if err != nil {
|
||||
return "", internal.NewInvalidSignatureError(fmt.Sprintf("invalid ASN.1 in OIDC issuer v2 extension: %v", err))
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return "", internal.NewInvalidSignatureError("invalid ASN.1 in OIDC issuer v2 extension, trailing data")
|
||||
}
|
||||
gotOIDCIssuer2 = true
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case gotOIDCIssuer1 && gotOIDCIssuer2:
|
||||
if oidcIssuer1 != oidcIssuer2 {
|
||||
return "", internal.NewInvalidSignatureError(fmt.Sprintf("inconsistent OIDC issuer extension values: v1 %#v, v2 %#v",
|
||||
oidcIssuer1, oidcIssuer2))
|
||||
}
|
||||
return oidcIssuer1, nil
|
||||
case gotOIDCIssuer1:
|
||||
return oidcIssuer1, nil
|
||||
case gotOIDCIssuer2:
|
||||
return oidcIssuer2, nil
|
||||
default:
|
||||
return "", internal.NewInvalidSignatureError("Fulcio certificate is missing the issuer extension")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fulcioTrustRoot) verifyFulcioCertificateAtTime(relevantTime time.Time, untrustedCertificateBytes []byte, untrustedIntermediateChainBytes []byte) (crypto.PublicKey, error) {
|
||||
// == Verify the certificate is correctly signed
|
||||
var untrustedIntermediatePool *x509.CertPool // = nil
|
||||
@@ -113,24 +165,9 @@ func (f *fulcioTrustRoot) verifyFulcioCertificateAtTime(relevantTime time.Time,
|
||||
// make the SCT (and all of Rekor apart from the trusted timestamp) unnecessary.
|
||||
|
||||
// == Validate the recorded OIDC issuer
|
||||
gotOIDCIssuer := false
|
||||
var oidcIssuer string
|
||||
// certificate.ParseExtensions doesn’t reject duplicate extensions.
|
||||
// Go 1.19 rejects duplicate extensions universally; but until we can require Go 1.19,
|
||||
// reject duplicates manually. With Go 1.19, we could call certificate.ParseExtensions again.
|
||||
for _, untrustedExt := range untrustedCertificate.Extensions {
|
||||
if untrustedExt.Id.Equal(certificate.OIDIssuer) {
|
||||
if gotOIDCIssuer {
|
||||
// Coverage: This is unreachable in Go ≥1.19, which rejects certificates with duplicate extensions
|
||||
// already in ParseCertificate.
|
||||
return nil, internal.NewInvalidSignatureError("Fulcio certificate has a duplicate OIDC issuer extension")
|
||||
}
|
||||
oidcIssuer = string(untrustedExt.Value)
|
||||
gotOIDCIssuer = true
|
||||
}
|
||||
}
|
||||
if !gotOIDCIssuer {
|
||||
return nil, internal.NewInvalidSignatureError("Fulcio certificate is missing the issuer extension")
|
||||
oidcIssuer, err := fulcioIssuerInCertificate(untrustedCertificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oidcIssuer != f.oidcIssuer {
|
||||
return nil, internal.NewInvalidSignatureError(fmt.Sprintf("Unexpected Fulcio OIDC issuer %q", oidcIssuer))
|
||||
|
||||
+8
-4
@@ -33,8 +33,10 @@ import (
|
||||
// limitations under the License.
|
||||
|
||||
const (
|
||||
// from sigstore/cosign/pkg/cosign.sigstorePrivateKeyPemType.
|
||||
sigstorePrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
|
||||
// from sigstore/cosign/pkg/cosign.CosignPrivateKeyPemType.
|
||||
cosignPrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY"
|
||||
// from sigstore/cosign/pkg/cosign.SigstorePrivateKeyPemType.
|
||||
sigstorePrivateKeyPemType = "ENCRYPTED SIGSTORE PRIVATE KEY"
|
||||
)
|
||||
|
||||
// from sigstore/cosign/pkg/cosign.loadPrivateKey
|
||||
@@ -45,7 +47,7 @@ func loadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
|
||||
if p == nil {
|
||||
return nil, errors.New("invalid pem block")
|
||||
}
|
||||
if p.Type != sigstorePrivateKeyPemType {
|
||||
if p.Type != sigstorePrivateKeyPemType && p.Type != cosignPrivateKeyPemType {
|
||||
return nil, fmt.Errorf("unsupported pem type: %s", p.Type)
|
||||
}
|
||||
|
||||
@@ -86,7 +88,9 @@ func marshalKeyPair(privateKey crypto.PrivateKey, publicKey crypto.PublicKey, pa
|
||||
// store in PEM format
|
||||
privBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Bytes: encBytes,
|
||||
Type: sigstorePrivateKeyPemType,
|
||||
// Use the older “COSIGN” type name; as of 2023-03-30 cosign’s main branch generates “SIGSTORE” types,
|
||||
// but a version of cosign that can accept them has not yet been released.
|
||||
Type: cosignPrivateKeyPemType,
|
||||
})
|
||||
|
||||
// Now do the public key
|
||||
|
||||
+1
-1
@@ -293,7 +293,7 @@ func buildLayerInfosForCopy(manifestInfos []manifest.LayerInfo, physicalInfos []
|
||||
if nextPhysical >= len(physicalInfos) {
|
||||
return nil, fmt.Errorf("expected more than %d physical layers to exist", len(physicalInfos))
|
||||
}
|
||||
res[i] = physicalInfos[nextPhysical]
|
||||
res[i] = physicalInfos[nextPhysical] // FIXME? Should we preserve more data in manifestInfos? Notably the current approach correctly removes zstd:chunked metadata annotations.
|
||||
nextPhysical++
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -6,12 +6,12 @@ const (
|
||||
// VersionMajor is for an API incompatible changes
|
||||
VersionMajor = 5
|
||||
// VersionMinor is for functionality in a backwards-compatible manner
|
||||
VersionMinor = 24
|
||||
VersionMinor = 25
|
||||
// VersionPatch is for backwards-compatible bug fixes
|
||||
VersionPatch = 3
|
||||
VersionPatch = 0
|
||||
|
||||
// VersionDev indicates development branch. Releases will be empty string.
|
||||
VersionDev = "-dev"
|
||||
VersionDev = ""
|
||||
)
|
||||
|
||||
// Version is the specification version that the package types support.
|
||||
|
||||
+13
-13
@@ -17,15 +17,15 @@ env:
|
||||
####
|
||||
#### Cache-image names to test with (double-quotes around names are critical)
|
||||
###
|
||||
FEDORA_NAME: "fedora-37" ### 20230120t152650z-f37f36u2204
|
||||
UBUNTU_NAME: "ubuntu-2204" ### 20230120t152650z-f37f36u2204
|
||||
FEDORA_NAME: "fedora-37"
|
||||
DEBIAN_NAME: "debian-12"
|
||||
|
||||
# GCE project where images live
|
||||
IMAGE_PROJECT: "libpod-218412"
|
||||
# VM Image built in containers/automation_images
|
||||
IMAGE_SUFFIX: "c20230120t152650z-f37f36u2204"
|
||||
IMAGE_SUFFIX: "c20230405t152256z-f37f36d12"
|
||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
|
||||
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
|
||||
|
||||
####
|
||||
#### Command variables to help avoid duplication
|
||||
@@ -67,7 +67,7 @@ linux_testing: &linux_testing
|
||||
always:
|
||||
df_script: '${_DFCMD} || true'
|
||||
rh_audit_log_script: '${_RAUDITCMD} || true'
|
||||
ubuntu_audit_log_script: '${_UAUDITCMD} || true'
|
||||
debian_audit_log_script: '${_UAUDITCMD} || true'
|
||||
journal_log_script: '${_JOURNALCMD} || true'
|
||||
|
||||
|
||||
@@ -95,13 +95,13 @@ fedora_testing_task: &fedora_testing
|
||||
|
||||
|
||||
# aufs was dropped between 20.04 and 22.04, can't test it
|
||||
ubuntu_testing_task: &ubuntu_testing
|
||||
debian_testing_task: &debian_testing
|
||||
<<: *linux_testing
|
||||
alias: ubuntu_testing
|
||||
alias: debian_testing
|
||||
name: *std_test_name
|
||||
env:
|
||||
OS_NAME: "${UBUNTU_NAME}"
|
||||
VM_IMAGE: "${UBUNTU_CACHE_IMAGE_NAME}"
|
||||
OS_NAME: "${DEBIAN_NAME}"
|
||||
VM_IMAGE: "${DEBIAN_CACHE_IMAGE_NAME}"
|
||||
# Not all $TEST_DRIVER combinations valid for all $VM_IMAGE types.
|
||||
matrix:
|
||||
- env:
|
||||
@@ -122,7 +122,7 @@ lint_task:
|
||||
env:
|
||||
CIRRUS_WORKING_DIR: "/go/src/github.com/containers/storage"
|
||||
container:
|
||||
image: golang:1.17
|
||||
image: golang
|
||||
modules_cache:
|
||||
fingerprint_script: cat go.sum
|
||||
folder: $GOPATH/pkg/mod
|
||||
@@ -145,7 +145,7 @@ meta_task:
|
||||
# Space-separated list of images used by this repository state
|
||||
IMGNAMES: |-
|
||||
${FEDORA_CACHE_IMAGE_NAME}
|
||||
${UBUNTU_CACHE_IMAGE_NAME}
|
||||
${DEBIAN_CACHE_IMAGE_NAME}
|
||||
BUILDID: "${CIRRUS_BUILD_ID}"
|
||||
REPOREF: "${CIRRUS_CHANGE_IN_REPO}"
|
||||
GCPJSON: ENCRYPTED[244a93fe8b386b48b96f748342bf741350e43805eee81dd04b45093bdf737e540b993fc735df41f131835fa0f9b65826]
|
||||
@@ -158,7 +158,7 @@ meta_task:
|
||||
|
||||
vendor_task:
|
||||
container:
|
||||
image: golang:1.17
|
||||
image: golang
|
||||
modules_cache:
|
||||
fingerprint_script: cat go.sum
|
||||
folder: $GOPATH/pkg/mod
|
||||
@@ -177,7 +177,7 @@ success_task:
|
||||
depends_on:
|
||||
- lint
|
||||
- fedora_testing
|
||||
- ubuntu_testing
|
||||
- debian_testing
|
||||
- meta
|
||||
- vendor
|
||||
- cross
|
||||
|
||||
+35
-1
@@ -2,36 +2,70 @@
|
||||
run:
|
||||
concurrency: 6
|
||||
deadline: 5m
|
||||
skip-dirs-use-default: true
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- cyclop
|
||||
- deadcode
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exhaustivestruct
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- funlen
|
||||
- gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- godox
|
||||
- goerr113
|
||||
- gofumpt
|
||||
- golint
|
||||
- gomnd
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ifshort
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- interfacebloat
|
||||
- ireturn
|
||||
- lll
|
||||
- maintidx
|
||||
- maligned
|
||||
- misspell
|
||||
- musttag
|
||||
- nakedret
|
||||
- nestif
|
||||
- nlreturn
|
||||
- nolintlint
|
||||
- nonamedreturns
|
||||
- nosnakecase
|
||||
- paralleltest
|
||||
- prealloc
|
||||
- predeclared
|
||||
- rowserrcheck
|
||||
- scopelint
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- stylecheck
|
||||
- tagliatelle
|
||||
- testpackage
|
||||
- thelper
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- varnamelen
|
||||
- wastedassign
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
- wsl
|
||||
+3
-3
@@ -26,7 +26,7 @@ NATIVETAGS :=
|
||||
AUTOTAGS := $(shell ./hack/btrfs_tag.sh) $(shell ./hack/libdm_tag.sh) $(shell ./hack/libsubid_tag.sh)
|
||||
BUILDFLAGS := -tags "$(AUTOTAGS) $(TAGS)" $(FLAGS)
|
||||
GO ?= go
|
||||
TESTFLAGS := $(shell go test -race $(BUILDFLAGS) ./pkg/stringutils 2>&1 > /dev/null && echo -race)
|
||||
TESTFLAGS := $(shell $(GO) test -race $(BUILDFLAGS) ./pkg/stringutils 2>&1 > /dev/null && echo -race)
|
||||
|
||||
# Go module support: set `-mod=vendor` to use the vendored sources
|
||||
ifeq ($(shell $(GO) help mod >/dev/null 2>&1 && echo true), true)
|
||||
@@ -93,9 +93,9 @@ help: ## this help
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-z A-Z_-]+:.*?## / {gsub(" ",",",$$1);gsub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-21s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
|
||||
vendor-in-container:
|
||||
podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src golang:1.17 make vendor
|
||||
podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src golang make vendor
|
||||
|
||||
vendor:
|
||||
$(GO) mod tidy -compat=1.17
|
||||
$(GO) mod tidy
|
||||
$(GO) mod vendor
|
||||
$(GO) mod verify
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.45.5-dev
|
||||
1.46.1
|
||||
+38
-15
@@ -107,13 +107,13 @@ type rwContainerStore interface {
|
||||
// stopReading releases locks obtained by startReading.
|
||||
stopReading()
|
||||
|
||||
// Create creates a container that has a specified ID (or generates a
|
||||
// create creates a container that has a specified ID (or generates a
|
||||
// random one if an empty value is supplied) and optional names,
|
||||
// based on the specified image, using the specified layer as its
|
||||
// read-write layer.
|
||||
// The maps in the container's options structure are recorded for the
|
||||
// convenience of the caller, nothing more.
|
||||
Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error)
|
||||
create(id string, names []string, image, layer string, options *ContainerOptions) (*Container, error)
|
||||
|
||||
// updateNames modifies names associated with a container based on (op, names).
|
||||
updateNames(id string, names []string, op updateNameOperation) error
|
||||
@@ -411,7 +411,7 @@ func (r *containerStore) GarbageCollect() error {
|
||||
for _, entry := range entries {
|
||||
id := entry.Name()
|
||||
// Does it look like a datadir directory?
|
||||
if !entry.IsDir() || !nameLooksLikeID(id) {
|
||||
if !entry.IsDir() || stringid.ValidateID(id) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -651,7 +651,10 @@ func (r *containerStore) SetFlag(id string, flag string, value interface{}) erro
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *containerStore) Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (container *Container, err error) {
|
||||
func (r *containerStore) create(id string, names []string, image, layer string, options *ContainerOptions) (container *Container, err error) {
|
||||
if options == nil {
|
||||
options = &ContainerOptions{}
|
||||
}
|
||||
if id == "" {
|
||||
id = stringid.GenerateRandomID()
|
||||
_, idInUse := r.byid[id]
|
||||
@@ -663,13 +666,7 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||
if _, idInUse := r.byid[id]; idInUse {
|
||||
return nil, ErrDuplicateID
|
||||
}
|
||||
if options.MountOpts != nil {
|
||||
options.Flags[mountOptsFlag] = append([]string{}, options.MountOpts...)
|
||||
}
|
||||
if options.Volatile {
|
||||
options.Flags[volatileFlag] = true
|
||||
}
|
||||
names = dedupeNames(names)
|
||||
names = dedupeStrings(names)
|
||||
for _, name := range names {
|
||||
if _, nameInUse := r.byname[name]; nameInUse {
|
||||
return nil, fmt.Errorf("the container name %q is already in use by %s. You have to remove that container to be able to reuse that name: %w", name, r.byname[name].ID, ErrDuplicateName)
|
||||
@@ -686,7 +683,7 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||
Names: names,
|
||||
ImageID: image,
|
||||
LayerID: layer,
|
||||
Metadata: metadata,
|
||||
Metadata: options.Metadata,
|
||||
BigDataNames: []string{},
|
||||
BigDataSizes: make(map[string]int64),
|
||||
BigDataDigests: make(map[string]digest.Digest),
|
||||
@@ -696,16 +693,42 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||
GIDMap: copyIDMap(options.GIDMap),
|
||||
volatileStore: options.Volatile,
|
||||
}
|
||||
if options.MountOpts != nil {
|
||||
container.Flags[mountOptsFlag] = append([]string{}, options.MountOpts...)
|
||||
}
|
||||
if options.Volatile {
|
||||
container.Flags[volatileFlag] = true
|
||||
}
|
||||
r.containers = append(r.containers, container)
|
||||
r.byid[id] = container
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in
|
||||
// that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here
|
||||
// would be too risky.
|
||||
_ = r.idindex.Add(id)
|
||||
r.byid[id] = container
|
||||
r.bylayer[layer] = container
|
||||
for _, name := range names {
|
||||
r.byname[name] = container
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// now that the in-memory structures know about the new
|
||||
// record, we can use regular Delete() to clean up if
|
||||
// anything breaks from here on out
|
||||
if e := r.Delete(id); e != nil {
|
||||
logrus.Debugf("while cleaning up partially-created container %q we failed to create: %v", id, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = r.saveFor(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range options.BigData {
|
||||
if err = r.SetBigData(id, item.Key, item.Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
container = copyContainer(container)
|
||||
return container, err
|
||||
}
|
||||
|
||||
+1
-1
@@ -239,7 +239,7 @@ func (a *Driver) Status() [][2]string {
|
||||
|
||||
// Metadata not implemented
|
||||
func (a *Driver) Metadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
|
||||
// Exists returns true if the given id is registered with
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ func (d *Driver) Status() [][2]string {
|
||||
|
||||
// Metadata returns empty metadata for this driver.
|
||||
func (d *Driver) Metadata(id string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
|
||||
// Cleanup unmounts the home directory.
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ const (
|
||||
)
|
||||
|
||||
// CopyRegularToFile copies the content of a file to another
|
||||
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { // nolint: golint
|
||||
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { // nolint: revive,golint
|
||||
srcFile, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -73,7 +73,7 @@ func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, c
|
||||
}
|
||||
|
||||
// CopyRegular copies the content of a file to another
|
||||
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { // nolint: golint
|
||||
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { // nolint: revive,golint
|
||||
// If the destination file already exists, we shouldn't blow it away
|
||||
dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, fileinfo.Mode())
|
||||
if err != nil {
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ func DirCopy(srcDir, dstDir string, _ Mode, _ bool) error {
|
||||
}
|
||||
|
||||
// CopyRegularToFile copies the content of a file to another
|
||||
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint: revive // "func name will be used as copy.CopyRegularToFile by other packages, and that stutters"
|
||||
func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint: revive,golint // "func name will be used as copy.CopyRegularToFile by other packages, and that stutters"
|
||||
f, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -36,6 +36,6 @@ func CopyRegularToFile(srcPath string, dstFile *os.File, fileinfo os.FileInfo, c
|
||||
}
|
||||
|
||||
// CopyRegular copies the content of a file to another
|
||||
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint:revive // "func name will be used as copy.CopyRegular by other packages, and that stutters"
|
||||
func CopyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRange, copyWithFileClone *bool) error { //nolint:revive,golint // "func name will be used as copy.CopyRegular by other packages, and that stutters"
|
||||
return chrootarchive.NewArchiver(nil).CopyWithTar(srcPath, dstPath)
|
||||
}
|
||||
+3
-3
@@ -48,7 +48,7 @@ func validateLVMConfig(cfg directLVMConfig) error {
|
||||
func checkDevAvailable(dev string) error {
|
||||
lvmScan, err := exec.LookPath("lvmdiskscan")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find lvmdiskscan")
|
||||
logrus.Debugf("could not find lvmdiskscan: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func checkDevAvailable(dev string) error {
|
||||
func checkDevInVG(dev string) error {
|
||||
pvDisplay, err := exec.LookPath("pvdisplay")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find pvdisplay")
|
||||
logrus.Debugf("could not find pvdisplay: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func checkDevInVG(dev string) error {
|
||||
func checkDevHasFS(dev string) error {
|
||||
blkid, err := exec.LookPath("blkid")
|
||||
if err != nil {
|
||||
logrus.Debug("could not find blkid")
|
||||
logrus.Debugf("could not find blkid %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -460,7 +460,7 @@ func (devices *DeviceSet) loadDeviceFilesOnStart() error {
|
||||
|
||||
var scan = func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
logrus.Debugf("devmapper: Can't walk the file %s", path)
|
||||
logrus.Debugf("devmapper: Can't walk the file %s: %v", path, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2487,10 +2487,11 @@ func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSec
|
||||
var params string
|
||||
_, sizeInSectors, _, params, err = devicemapper.GetStatus(devName)
|
||||
if err != nil {
|
||||
logrus.Debugf("could not find devicemapper status: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil {
|
||||
return
|
||||
if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err != nil {
|
||||
logrus.Debugf("could not find scanf devicemapper status: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+2
-2
@@ -52,8 +52,8 @@ type MountOpts struct {
|
||||
// Mount label is the MAC Labels to assign to mount point (SELINUX)
|
||||
MountLabel string
|
||||
// UidMaps & GidMaps are the User Namespace mappings to be assigned to content in the mount point
|
||||
UidMaps []idtools.IDMap //nolint: golint,revive
|
||||
GidMaps []idtools.IDMap //nolint: golint
|
||||
UidMaps []idtools.IDMap //nolint: revive,golint
|
||||
GidMaps []idtools.IDMap //nolint: revive,golint
|
||||
Options []string
|
||||
|
||||
// Volatile specifies whether the container storage can be optimized
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ func (c *defaultChecker) IsMounted(path string) bool {
|
||||
}
|
||||
|
||||
// Mounted checks if the given path is mounted as the fs type
|
||||
//Solaris supports only ZFS for now
|
||||
// Solaris supports only ZFS for now
|
||||
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||
|
||||
cs := C.CString(filepath.Dir(mountPath))
|
||||
|
||||
+21
-20
@@ -17,7 +17,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unicode"
|
||||
|
||||
graphdriver "github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/drivers/overlayutils"
|
||||
@@ -30,6 +29,7 @@ import (
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/mount"
|
||||
"github.com/containers/storage/pkg/parsers"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/system"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
units "github.com/docker/go-units"
|
||||
@@ -314,9 +314,6 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
|
||||
}
|
||||
fsName, ok := graphdriver.FsNames[fsMagic]
|
||||
if !ok {
|
||||
if opts.mountProgram == "" {
|
||||
return nil, fmt.Errorf("filesystem type %#x reported for %s is not supported with 'overlay': %w", fsMagic, filepath.Dir(home), graphdriver.ErrIncompatibleFS)
|
||||
}
|
||||
fsName = "<unknown>"
|
||||
}
|
||||
backingFs = fsName
|
||||
@@ -549,6 +546,9 @@ func parseOptions(options []string) (*overlayOptions, error) {
|
||||
case "skip_mount_home":
|
||||
logrus.Debugf("overlay: skip_mount_home=%s", val)
|
||||
o.skipMountHome, err = strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "ignore_chown_errors":
|
||||
logrus.Debugf("overlay: ignore_chown_errors=%s", val)
|
||||
o.ignoreChownErrors, err = strconv.ParseBool(val)
|
||||
@@ -685,8 +685,11 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
|
||||
|
||||
// Try a test mount in the specific location we're looking at using.
|
||||
mergedDir := filepath.Join(layerDir, "merged")
|
||||
mergedSubdir := filepath.Join(mergedDir, "subdir")
|
||||
lower1Dir := filepath.Join(layerDir, "lower1")
|
||||
lower2Dir := filepath.Join(layerDir, "lower2")
|
||||
lower2Subdir := filepath.Join(lower2Dir, "subdir")
|
||||
lower2SubdirFile := filepath.Join(lower2Subdir, "file")
|
||||
upperDir := filepath.Join(layerDir, "upper")
|
||||
workDir := filepath.Join(layerDir, "work")
|
||||
defer func() {
|
||||
@@ -700,8 +703,15 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
|
||||
_ = idtools.MkdirAs(mergedDir, 0700, rootUID, rootGID)
|
||||
_ = idtools.MkdirAs(lower1Dir, 0700, rootUID, rootGID)
|
||||
_ = idtools.MkdirAs(lower2Dir, 0700, rootUID, rootGID)
|
||||
_ = idtools.MkdirAs(lower2Subdir, 0700, rootUID, rootGID)
|
||||
_ = idtools.MkdirAs(upperDir, 0700, rootUID, rootGID)
|
||||
_ = idtools.MkdirAs(workDir, 0700, rootUID, rootGID)
|
||||
f, err := os.Create(lower2SubdirFile)
|
||||
if err != nil {
|
||||
logrus.Debugf("Unable to create test file: %v", err)
|
||||
return supportsDType, fmt.Errorf("unable to create test file: %w", err)
|
||||
}
|
||||
f.Close()
|
||||
flags := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", lower1Dir, lower2Dir, upperDir, workDir)
|
||||
if selinux.GetEnabled() &&
|
||||
selinux.SecurityCheckContext(selinuxLabelTest) == nil {
|
||||
@@ -721,6 +731,10 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
|
||||
if len(flags) < unix.Getpagesize() {
|
||||
err := unix.Mount("overlay", mergedDir, "overlay", 0, flags)
|
||||
if err == nil {
|
||||
if err = os.RemoveAll(mergedSubdir); err != nil {
|
||||
logrus.StandardLogger().Logf(logLevel, "overlay: removing an item from the merged directory failed: %v", err)
|
||||
return supportsDType, fmt.Errorf("kernel returned %v when we tried to delete an item in the merged directory: %w", err, graphdriver.ErrNotSupported)
|
||||
}
|
||||
logrus.Debugf("overlay: test mount with multiple lowers succeeded")
|
||||
return supportsDType, nil
|
||||
}
|
||||
@@ -1427,7 +1441,6 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
|
||||
perms = os.FileMode(st2.Mode())
|
||||
permsKnown = true
|
||||
}
|
||||
l = lower
|
||||
break
|
||||
}
|
||||
lower = ""
|
||||
@@ -1509,7 +1522,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
|
||||
}
|
||||
}
|
||||
|
||||
if !disableShifting && len(options.UidMaps) > 0 && len(options.GidMaps) > 0 {
|
||||
if !disableShifting && len(options.UidMaps) > 0 && len(options.GidMaps) > 0 && d.options.mountProgram == "" {
|
||||
var newAbsDir []string
|
||||
mappedRoot := filepath.Join(d.home, id, "mapped")
|
||||
if err := os.MkdirAll(mappedRoot, 0700); err != nil {
|
||||
@@ -1706,18 +1719,6 @@ func (d *Driver) Exists(id string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func nameLooksLikeID(name string) bool {
|
||||
if len(name) != 64 {
|
||||
return false
|
||||
}
|
||||
for _, c := range name {
|
||||
if !unicode.Is(unicode.ASCII_Hex_Digit, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// List layers (not including additional image stores)
|
||||
func (d *Driver) ListLayers() ([]string, error) {
|
||||
entries, err := os.ReadDir(d.home)
|
||||
@@ -1730,7 +1731,7 @@ func (d *Driver) ListLayers() ([]string, error) {
|
||||
for _, entry := range entries {
|
||||
id := entry.Name()
|
||||
// Does it look like a datadir directory?
|
||||
if !entry.IsDir() || !nameLooksLikeID(id) {
|
||||
if !entry.IsDir() || stringid.ValidateID(id) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1827,7 +1828,7 @@ func (d *Driver) ApplyDiffWithDiffer(id, parent string, options *graphdriver.App
|
||||
idMappings = &idtools.IDMappings{}
|
||||
}
|
||||
|
||||
applyDir := ""
|
||||
var applyDir string
|
||||
|
||||
if id == "" {
|
||||
err := os.MkdirAll(d.getStagingDir(), 0700)
|
||||
|
||||
+2
-14
@@ -8,13 +8,13 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
graphdriver "github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/directory"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/parsers"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/system"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -266,18 +266,6 @@ func (d *Driver) Exists(id string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func nameLooksLikeID(name string) bool {
|
||||
if len(name) != 64 {
|
||||
return false
|
||||
}
|
||||
for _, c := range name {
|
||||
if !unicode.Is(unicode.ASCII_Hex_Digit, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// List layers (not including additional image stores)
|
||||
func (d *Driver) ListLayers() ([]string, error) {
|
||||
entries, err := os.ReadDir(d.homes[0])
|
||||
@@ -290,7 +278,7 @@ func (d *Driver) ListLayers() ([]string, error) {
|
||||
for _, entry := range entries {
|
||||
id := entry.Name()
|
||||
// Does it look like a datadir directory?
|
||||
if !entry.IsDir() || !nameLooksLikeID(id) {
|
||||
if !entry.IsDir() || stringid.ValidateID(id) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
+53
-24
@@ -137,10 +137,10 @@ type rwImageStore interface {
|
||||
// stopWriting releases locks obtained by startWriting.
|
||||
stopWriting()
|
||||
|
||||
// Create creates an image that has a specified ID (or a random one) and
|
||||
// create creates an image that has a specified ID (or a random one) and
|
||||
// optional names, using the specified layer as its topmost (hopefully
|
||||
// read-only) layer. That layer can be referenced by multiple images.
|
||||
Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (*Image, error)
|
||||
create(id string, names []string, layer string, options ImageOptions) (*Image, error)
|
||||
|
||||
// updateNames modifies names associated with an image based on (op, names).
|
||||
// The values are expected to be valid normalized
|
||||
@@ -414,7 +414,7 @@ func (r *imageStore) GarbageCollect() error {
|
||||
for _, entry := range entries {
|
||||
id := entry.Name()
|
||||
// Does it look like a datadir directory?
|
||||
if !entry.IsDir() || !nameLooksLikeID(id) {
|
||||
if !entry.IsDir() || stringid.ValidateID(id) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -688,7 +688,7 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *imageStore) Create(id string, names []string, layer, metadata string, created time.Time, searchableDigest digest.Digest) (image *Image, err error) {
|
||||
func (r *imageStore) create(id string, names []string, layer string, options ImageOptions) (image *Image, err error) {
|
||||
if !r.lockfile.IsReadWrite() {
|
||||
return nil, fmt.Errorf("not allowed to create new images at %q: %w", r.imagespath(), ErrStoreIsReadOnly)
|
||||
}
|
||||
@@ -703,36 +703,38 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
|
||||
if _, idInUse := r.byid[id]; idInUse {
|
||||
return nil, fmt.Errorf("an image with ID %q already exists: %w", id, ErrDuplicateID)
|
||||
}
|
||||
names = dedupeNames(names)
|
||||
names = dedupeStrings(names)
|
||||
for _, name := range names {
|
||||
if image, nameInUse := r.byname[name]; nameInUse {
|
||||
return nil, fmt.Errorf("image name %q is already associated with image %q: %w", name, image.ID, ErrDuplicateName)
|
||||
}
|
||||
}
|
||||
if created.IsZero() {
|
||||
created = time.Now().UTC()
|
||||
}
|
||||
|
||||
image = &Image{
|
||||
ID: id,
|
||||
Digest: searchableDigest,
|
||||
Digests: nil,
|
||||
Digest: options.Digest,
|
||||
Digests: dedupeDigests(options.Digests),
|
||||
Names: names,
|
||||
NamesHistory: copyStringSlice(options.NamesHistory),
|
||||
TopLayer: layer,
|
||||
Metadata: metadata,
|
||||
Metadata: options.Metadata,
|
||||
BigDataNames: []string{},
|
||||
BigDataSizes: make(map[string]int64),
|
||||
BigDataDigests: make(map[string]digest.Digest),
|
||||
Created: created,
|
||||
Flags: make(map[string]interface{}),
|
||||
Created: options.CreationDate,
|
||||
Flags: copyStringInterfaceMap(options.Flags),
|
||||
}
|
||||
if image.Created.IsZero() {
|
||||
image.Created = time.Now().UTC()
|
||||
}
|
||||
err = image.recomputeDigests()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating digests for new image: %w", err)
|
||||
}
|
||||
r.images = append(r.images, image)
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in
|
||||
// that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here
|
||||
// would be too risky.
|
||||
_ = r.idindex.Add(id)
|
||||
r.byid[id] = image
|
||||
for _, name := range names {
|
||||
@@ -742,7 +744,28 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string, c
|
||||
list := r.bydigest[digest]
|
||||
r.bydigest[digest] = append(list, image)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// now that the in-memory structures know about the new
|
||||
// record, we can use regular Delete() to clean up if
|
||||
// anything breaks from here on out
|
||||
if e := r.Delete(id); e != nil {
|
||||
logrus.Debugf("while cleaning up partially-created image %q we failed to create: %v", id, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = r.Save()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range options.BigData {
|
||||
if item.Digest == "" {
|
||||
item.Digest = digest.Canonical.FromBytes(item.Data)
|
||||
}
|
||||
if err = r.setBigData(image, item.Key, item.Data, item.Digest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
image = copyImage(image)
|
||||
return image, err
|
||||
}
|
||||
@@ -797,7 +820,7 @@ func (r *imageStore) removeName(image *Image, name string) {
|
||||
|
||||
// The caller must hold r.inProcessLock for writing.
|
||||
func (i *Image) addNameToHistory(name string) {
|
||||
i.NamesHistory = dedupeNames(append([]string{name}, i.NamesHistory...))
|
||||
i.NamesHistory = dedupeStrings(append([]string{name}, i.NamesHistory...))
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
@@ -965,9 +988,6 @@ func imageSliceWithoutValue(slice []*Image, value *Image) []*Image {
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("can't set empty name for image big data item: %w", ErrInvalidBigDataName)
|
||||
}
|
||||
if !r.lockfile.IsReadWrite() {
|
||||
return fmt.Errorf("not allowed to save data items associated with images at %q: %w", r.imagespath(), ErrStoreIsReadOnly)
|
||||
}
|
||||
@@ -975,10 +995,7 @@ func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func
|
||||
if !ok {
|
||||
return fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown)
|
||||
}
|
||||
err := os.MkdirAll(r.datadir(image.ID), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
var newDigest digest.Digest
|
||||
if bigDataNameIsManifest(key) {
|
||||
if digestManifest == nil {
|
||||
@@ -990,6 +1007,18 @@ func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func
|
||||
} else {
|
||||
newDigest = digest.Canonical.FromBytes(data)
|
||||
}
|
||||
return r.setBigData(image, key, data, newDigest)
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *imageStore) setBigData(image *Image, key string, data []byte, newDigest digest.Digest) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("can't set empty name for image big data item: %w", ErrInvalidBigDataName)
|
||||
}
|
||||
err := os.MkdirAll(r.datadir(image.ID), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600)
|
||||
if err == nil {
|
||||
save := false
|
||||
|
||||
+87
-82
@@ -112,33 +112,33 @@ type Layer struct {
|
||||
Created time.Time `json:"created,omitempty"`
|
||||
|
||||
// CompressedDigest is the digest of the blob that was last passed to
|
||||
// ApplyDiff() or Put(), as it was presented to us.
|
||||
// ApplyDiff() or create(), as it was presented to us.
|
||||
CompressedDigest digest.Digest `json:"compressed-diff-digest,omitempty"`
|
||||
|
||||
// CompressedSize is the length of the blob that was last passed to
|
||||
// ApplyDiff() or Put(), as it was presented to us. If
|
||||
// ApplyDiff() or create(), as it was presented to us. If
|
||||
// CompressedDigest is not set, this should be treated as if it were an
|
||||
// uninitialized value.
|
||||
CompressedSize int64 `json:"compressed-size,omitempty"`
|
||||
|
||||
// UncompressedDigest is the digest of the blob that was last passed to
|
||||
// ApplyDiff() or Put(), after we decompressed it. Often referred to
|
||||
// ApplyDiff() or create(), after we decompressed it. Often referred to
|
||||
// as a DiffID.
|
||||
UncompressedDigest digest.Digest `json:"diff-digest,omitempty"`
|
||||
|
||||
// UncompressedSize is the length of the blob that was last passed to
|
||||
// ApplyDiff() or Put(), after we decompressed it. If
|
||||
// ApplyDiff() or create(), after we decompressed it. If
|
||||
// UncompressedDigest is not set, this should be treated as if it were
|
||||
// an uninitialized value.
|
||||
UncompressedSize int64 `json:"diff-size,omitempty"`
|
||||
|
||||
// CompressionType is the type of compression which we detected on the blob
|
||||
// that was last passed to ApplyDiff() or Put().
|
||||
// that was last passed to ApplyDiff() or create().
|
||||
CompressionType archive.Compression `json:"compression,omitempty"`
|
||||
|
||||
// UIDs and GIDs are lists of UIDs and GIDs used in the layer. This
|
||||
// field is only populated (i.e., will only contain one or more
|
||||
// entries) if the layer was created using ApplyDiff() or Put().
|
||||
// entries) if the layer was created using ApplyDiff() or create().
|
||||
UIDs []uint32 `json:"uidset,omitempty"`
|
||||
GIDs []uint32 `json:"gidset,omitempty"`
|
||||
|
||||
@@ -248,20 +248,15 @@ type rwLayerStore interface {
|
||||
// stopWriting releases locks obtained by startWriting.
|
||||
stopWriting()
|
||||
|
||||
// Create creates a new layer, optionally giving it a specified ID rather than
|
||||
// create creates a new layer, optionally giving it a specified ID rather than
|
||||
// a randomly-generated one, either inheriting data from another specified
|
||||
// layer or the empty base layer. The new layer can optionally be given names
|
||||
// and have an SELinux label specified for use when mounting it. Some
|
||||
// underlying drivers can accept a "size" option. At this time, most
|
||||
// underlying drivers do not themselves distinguish between writeable
|
||||
// and read-only layers.
|
||||
Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (*Layer, error)
|
||||
|
||||
// CreateWithFlags combines the functions of Create and SetFlag.
|
||||
CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error)
|
||||
|
||||
// Put combines the functions of CreateWithFlags and ApplyDiff.
|
||||
Put(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error)
|
||||
// and read-only layers. Returns the new layer structure and the size of the
|
||||
// diff which was applied to its parent to initialize its contents.
|
||||
create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, diff io.Reader) (*Layer, int64, error)
|
||||
|
||||
// updateNames modifies names associated with a layer based on (op, names).
|
||||
updateNames(id string, names []string, op updateNameOperation) error
|
||||
@@ -1186,8 +1181,10 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s
|
||||
|
||||
// TODO: check if necessary fields are filled
|
||||
r.layers = append(r.layers, layer)
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here would be too risky.
|
||||
// This can only fail on duplicate IDs, which shouldn’t happen — and in
|
||||
// that case the index is already in the desired state anyway.
|
||||
// Implementing recovery from an unlikely and unimportant failure here
|
||||
// would be too risky.
|
||||
_ = r.idindex.Add(id)
|
||||
r.byid[id] = layer
|
||||
for _, name := range names { // names got from the additional layer store won't be used
|
||||
@@ -1200,8 +1197,8 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s
|
||||
r.byuncompressedsum[layer.UncompressedDigest] = append(r.byuncompressedsum[layer.UncompressedDigest], layer.ID)
|
||||
}
|
||||
if err := r.saveFor(layer); err != nil {
|
||||
if err2 := r.driver.Remove(id); err2 != nil {
|
||||
logrus.Errorf("While recovering from a failure to save layers, error deleting layer %#v: %v", id, err2)
|
||||
if e := r.Delete(layer.ID); e != nil {
|
||||
logrus.Errorf("While recovering from a failure to save layers, error deleting layer %#v: %v", id, e)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -1209,7 +1206,10 @@ func (r *layerStore) PutAdditionalLayer(id string, parentLayer *Layer, names []s
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}, diff io.Reader) (*Layer, int64, error) {
|
||||
func (r *layerStore) create(id string, parentLayer *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, diff io.Reader) (layer *Layer, size int64, err error) {
|
||||
if moreOptions == nil {
|
||||
moreOptions = &LayerOptions{}
|
||||
}
|
||||
if !r.lockfile.IsReadWrite() {
|
||||
return nil, -1, fmt.Errorf("not allowed to create new layers at %q: %w", r.layerdir, ErrStoreIsReadOnly)
|
||||
}
|
||||
@@ -1230,7 +1230,7 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
if duplicateLayer, idInUse := r.byid[id]; idInUse {
|
||||
return duplicateLayer, -1, ErrDuplicateID
|
||||
}
|
||||
names = dedupeNames(names)
|
||||
names = dedupeStrings(names)
|
||||
for _, name := range names {
|
||||
if _, nameInUse := r.byname[name]; nameInUse {
|
||||
return nil, -1, ErrDuplicateName
|
||||
@@ -1252,7 +1252,6 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
templateTSdata []byte
|
||||
)
|
||||
if moreOptions.TemplateLayer != "" {
|
||||
var tserr error
|
||||
templateLayer, ok := r.lookup(moreOptions.TemplateLayer)
|
||||
if !ok {
|
||||
return nil, -1, ErrLayerUnknown
|
||||
@@ -1263,9 +1262,9 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
templateUncompressedDigest, templateUncompressedSize = templateLayer.UncompressedDigest, templateLayer.UncompressedSize
|
||||
templateCompressionType = templateLayer.CompressionType
|
||||
templateUIDs, templateGIDs = append([]uint32{}, templateLayer.UIDs...), append([]uint32{}, templateLayer.GIDs...)
|
||||
templateTSdata, tserr = os.ReadFile(r.tspath(templateLayer.ID))
|
||||
if tserr != nil && !os.IsNotExist(tserr) {
|
||||
return nil, -1, tserr
|
||||
templateTSdata, err = os.ReadFile(r.tspath(templateLayer.ID))
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, -1, err
|
||||
}
|
||||
} else {
|
||||
templateIDMappings = &idtools.IDMappings{}
|
||||
@@ -1279,9 +1278,10 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
selinux.ReserveLabel(mountLabel)
|
||||
}
|
||||
|
||||
// Before actually creating the layer, make a persistent record of it with incompleteFlag,
|
||||
// so that future processes have a chance to delete it.
|
||||
layer := &Layer{
|
||||
// Before actually creating the layer, make a persistent record of it
|
||||
// with the incomplete flag set, so that future processes have a chance
|
||||
// to clean up after it.
|
||||
layer = &Layer{
|
||||
ID: id,
|
||||
Parent: parent,
|
||||
Names: names,
|
||||
@@ -1295,98 +1295,109 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
CompressionType: templateCompressionType,
|
||||
UIDs: templateUIDs,
|
||||
GIDs: templateGIDs,
|
||||
Flags: make(map[string]interface{}),
|
||||
Flags: copyStringInterfaceMap(moreOptions.Flags),
|
||||
UIDMap: copyIDMap(moreOptions.UIDMap),
|
||||
GIDMap: copyIDMap(moreOptions.GIDMap),
|
||||
BigDataNames: []string{},
|
||||
volatileStore: moreOptions.Volatile,
|
||||
}
|
||||
layer.Flags[incompleteFlag] = true
|
||||
|
||||
r.layers = append(r.layers, layer)
|
||||
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||
// This is on various paths to recover from failures, so this should be robust against partially missing data.
|
||||
// This can only fail if the ID is already missing, which shouldn’t
|
||||
// happen — and in that case the index is already in the desired state
|
||||
// anyway. This is on various paths to recover from failures, so this
|
||||
// should be robust against partially missing data.
|
||||
_ = r.idindex.Add(id)
|
||||
r.byid[id] = layer
|
||||
for _, name := range names {
|
||||
r.byname[name] = layer
|
||||
}
|
||||
for flag, value := range flags {
|
||||
layer.Flags[flag] = value
|
||||
}
|
||||
layer.Flags[incompleteFlag] = true
|
||||
|
||||
succeeded := false
|
||||
cleanupFailureContext := ""
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
// On any error, try both removing the driver's data as well
|
||||
// as the in-memory layer record.
|
||||
if err2 := r.Delete(layer.ID); err2 != nil {
|
||||
if cleanupFailureContext == "" {
|
||||
cleanupFailureContext = "unknown: cleanupFailureContext not set at the failure site"
|
||||
}
|
||||
logrus.Errorf("While recovering from a failure (%s), error deleting layer %#v: %v", cleanupFailureContext, layer.ID, err2)
|
||||
if err != nil {
|
||||
// now that the in-memory structures know about the new
|
||||
// record, we can use regular Delete() to clean up if
|
||||
// anything breaks from here on out
|
||||
if cleanupFailureContext == "" {
|
||||
cleanupFailureContext = "unknown: cleanupFailureContext not set at the failure site"
|
||||
}
|
||||
if e := r.Delete(id); e != nil {
|
||||
logrus.Errorf("While recovering from a failure (%s), error deleting layer %#v: %v", cleanupFailureContext, id, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err := r.saveFor(layer)
|
||||
if err != nil {
|
||||
if err = r.saveFor(layer); err != nil {
|
||||
cleanupFailureContext = "saving incomplete layer metadata"
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
for _, item := range moreOptions.BigData {
|
||||
if err = r.setBigData(layer, item.Key, item.Data); err != nil {
|
||||
cleanupFailureContext = fmt.Sprintf("saving big data item %q", item.Key)
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
|
||||
idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap)
|
||||
opts := drivers.CreateOpts{
|
||||
MountLabel: mountLabel,
|
||||
StorageOpt: options,
|
||||
IDMappings: idMappings,
|
||||
}
|
||||
|
||||
if moreOptions.TemplateLayer != "" {
|
||||
if err := r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil {
|
||||
cleanupFailureContext = "creating a layer from template"
|
||||
if err = r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil {
|
||||
cleanupFailureContext = fmt.Sprintf("creating a layer from template layer %q", moreOptions.TemplateLayer)
|
||||
return nil, -1, fmt.Errorf("creating copy of template layer %q with ID %q: %w", moreOptions.TemplateLayer, id, err)
|
||||
}
|
||||
oldMappings = templateIDMappings
|
||||
} else {
|
||||
if writeable {
|
||||
if err := r.driver.CreateReadWrite(id, parent, &opts); err != nil {
|
||||
if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil {
|
||||
cleanupFailureContext = "creating a read-write layer"
|
||||
return nil, -1, fmt.Errorf("creating read-write layer with ID %q: %w", id, err)
|
||||
}
|
||||
} else {
|
||||
if err := r.driver.Create(id, parent, &opts); err != nil {
|
||||
if err = r.driver.Create(id, parent, &opts); err != nil {
|
||||
cleanupFailureContext = "creating a read-only layer"
|
||||
return nil, -1, fmt.Errorf("creating layer with ID %q: %w", id, err)
|
||||
return nil, -1, fmt.Errorf("creating read-only layer with ID %q: %w", id, err)
|
||||
}
|
||||
}
|
||||
oldMappings = parentMappings
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(oldMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(oldMappings.GIDs(), idMappings.GIDs()) {
|
||||
if err := r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil {
|
||||
if err = r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil {
|
||||
cleanupFailureContext = "in UpdateLayerIDMap"
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(templateTSdata) > 0 {
|
||||
if err := os.MkdirAll(filepath.Dir(r.tspath(id)), 0o700); err != nil {
|
||||
if err = os.MkdirAll(filepath.Dir(r.tspath(id)), 0o700); err != nil {
|
||||
cleanupFailureContext = "creating tar-split parent directory for a copy from template"
|
||||
return nil, -1, err
|
||||
}
|
||||
if err := ioutils.AtomicWriteFile(r.tspath(id), templateTSdata, 0o600); err != nil {
|
||||
if err = ioutils.AtomicWriteFile(r.tspath(id), templateTSdata, 0o600); err != nil {
|
||||
cleanupFailureContext = "creating a tar-split copy from template"
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
|
||||
var size int64 = -1
|
||||
size = -1
|
||||
if diff != nil {
|
||||
size, err = r.applyDiffWithOptions(layer.ID, moreOptions, diff)
|
||||
if err != nil {
|
||||
if size, err = r.applyDiffWithOptions(layer.ID, moreOptions, diff); err != nil {
|
||||
cleanupFailureContext = "applying layer diff"
|
||||
return nil, -1, err
|
||||
}
|
||||
} else {
|
||||
// applyDiffWithOptions in the `diff != nil` case handles this bit for us
|
||||
// applyDiffWithOptions() would have updated r.bycompressedsum
|
||||
// and r.byuncompressedsum for us, but if we used a template
|
||||
// layer, we didn't call it, so add the new layer as candidates
|
||||
// for searches for layers by checksum
|
||||
if layer.CompressedDigest != "" {
|
||||
r.bycompressedsum[layer.CompressedDigest] = append(r.bycompressedsum[layer.CompressedDigest], layer.ID)
|
||||
}
|
||||
@@ -1394,29 +1405,17 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
|
||||
r.byuncompressedsum[layer.UncompressedDigest] = append(r.byuncompressedsum[layer.UncompressedDigest], layer.ID)
|
||||
}
|
||||
}
|
||||
|
||||
delete(layer.Flags, incompleteFlag)
|
||||
err = r.saveFor(layer)
|
||||
if err != nil {
|
||||
if err = r.saveFor(layer); err != nil {
|
||||
cleanupFailureContext = "saving finished layer metadata"
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
layer = copyLayer(layer)
|
||||
succeeded = true
|
||||
return layer, size, err
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *layerStore) CreateWithFlags(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool, flags map[string]interface{}) (layer *Layer, err error) {
|
||||
layer, _, err = r.Put(id, parent, names, mountLabel, options, moreOptions, writeable, flags, nil)
|
||||
return layer, err
|
||||
}
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel string, options map[string]string, moreOptions *LayerOptions, writeable bool) (layer *Layer, err error) {
|
||||
return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil)
|
||||
}
|
||||
|
||||
// Requires startReading or startWriting.
|
||||
func (r *layerStore) Mounted(id string) (int, error) {
|
||||
if !r.lockfile.IsReadWrite() {
|
||||
@@ -1677,9 +1676,6 @@ func (r *layerStore) BigData(id, key string) (io.ReadCloser, error) {
|
||||
|
||||
// Requires startWriting.
|
||||
func (r *layerStore) SetBigData(id, key string, data io.Reader) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("can't set empty name for layer big data item: %w", ErrInvalidBigDataName)
|
||||
}
|
||||
if !r.lockfile.IsReadWrite() {
|
||||
return fmt.Errorf("not allowed to save data items associated with layers at %q: %w", r.layerdir, ErrStoreIsReadOnly)
|
||||
}
|
||||
@@ -1687,6 +1683,13 @@ func (r *layerStore) SetBigData(id, key string, data io.Reader) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("locating layer with ID %q to write bigdata: %w", id, ErrLayerUnknown)
|
||||
}
|
||||
return r.setBigData(layer, key, data)
|
||||
}
|
||||
|
||||
func (r *layerStore) setBigData(layer *Layer, key string, data io.Reader) error {
|
||||
if key == "" {
|
||||
return fmt.Errorf("can't set empty name for layer big data item: %w", ErrInvalidBigDataName)
|
||||
}
|
||||
err := os.MkdirAll(r.datadir(layer.ID), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1759,7 +1762,9 @@ func (r *layerStore) tspath(id string) string {
|
||||
// layerHasIncompleteFlag returns true if layer.Flags contains an incompleteFlag set to true
|
||||
// The caller must hold r.inProcessLock for reading.
|
||||
func layerHasIncompleteFlag(layer *Layer) bool {
|
||||
// layer.Flags[…] is defined to succeed and return ok == false if Flags == nil
|
||||
if layer.Flags == nil {
|
||||
return false
|
||||
}
|
||||
if flagValue, ok := layer.Flags[incompleteFlag]; ok {
|
||||
if b, ok := flagValue.(bool); ok && b {
|
||||
return true
|
||||
@@ -1788,20 +1793,21 @@ func (r *layerStore) deleteInternal(id string) error {
|
||||
}
|
||||
}
|
||||
// We never unset incompleteFlag; below, we remove the entire object from r.layers.
|
||||
|
||||
id = layer.ID
|
||||
if err := r.driver.Remove(id); err != nil {
|
||||
if err := r.driver.Remove(id); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
os.Remove(r.tspath(id))
|
||||
os.RemoveAll(r.datadir(id))
|
||||
delete(r.byid, id)
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
// This can only fail if the ID is already missing, which shouldn’t happen — and in that case the index is already in the desired state anyway.
|
||||
// The store’s Delete method is used on various paths to recover from failures, so this should be robust against partially missing data.
|
||||
// This can only fail if the ID is already missing, which shouldn’t
|
||||
// happen — and in that case the index is already in the desired state
|
||||
// anyway. The store’s Delete method is used on various paths to
|
||||
// recover from failures, so this should be robust against partially
|
||||
// missing data.
|
||||
_ = r.idindex.Delete(id)
|
||||
mountLabel := layer.MountLabel
|
||||
if layer.MountPoint != "" {
|
||||
@@ -1835,7 +1841,6 @@ func (r *layerStore) deleteInternal(id string) error {
|
||||
selinux.ReleaseLabel(mountLabel)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -53,7 +53,7 @@ func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi
|
||||
}
|
||||
// If there are no lower layers, then it can't have been deleted in this layer.
|
||||
if len(o.rolayers) == 0 {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
// At this point, we have a directory that's opaque. If it appears in one of the lower
|
||||
// layers, then it was newly-created here, so it wasn't also deleted here.
|
||||
@@ -66,7 +66,7 @@ func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi
|
||||
if statErr == nil {
|
||||
if stat.Mode()&os.ModeCharDevice != 0 {
|
||||
if isWhiteOut(stat) {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
}
|
||||
// It's not whiteout, so it was there in the older layer, so we need to
|
||||
@@ -100,7 +100,7 @@ func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi
|
||||
// original directory wasn't inherited into this layer,
|
||||
// so we don't need to emit whiteout for it.
|
||||
if isWhiteOut(stat) {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ type walker struct {
|
||||
dir2 string
|
||||
root1 *FileInfo
|
||||
root2 *FileInfo
|
||||
idmap1 *idtools.IDMappings
|
||||
idmap2 *idtools.IDMappings
|
||||
idmap1 *idtools.IDMappings //nolint:unused
|
||||
idmap2 *idtools.IDMappings //nolint:unused
|
||||
}
|
||||
|
||||
// collectFileInfoForChanges returns a complete representation of the trees
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ import (
|
||||
// Generate("foo.txt", "hello world", "emptyfile")
|
||||
//
|
||||
// The above call will return an archive with 2 files:
|
||||
// * ./foo.txt with content "hello world"
|
||||
// * ./empty with empty content
|
||||
// - ./foo.txt with content "hello world"
|
||||
// - ./empty with empty content
|
||||
//
|
||||
// FIXME: stream content instead of buffering
|
||||
// FIXME: specify permissions and other archive metadata
|
||||
|
||||
+3
-2
@@ -361,7 +361,7 @@ func readMetadataFromCache(bigData io.Reader) (*metadata, error) {
|
||||
return nil, err
|
||||
}
|
||||
if version != cacheVersion {
|
||||
return nil, nil
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
if err := binary.Read(bigData, binary.LittleEndian, &tagLen); err != nil {
|
||||
return nil, err
|
||||
@@ -398,7 +398,8 @@ func prepareMetadata(manifest []byte) ([]*internal.FileMetadata, error) {
|
||||
toc, err := unmarshalToc(manifest)
|
||||
if err != nil {
|
||||
// ignore errors here. They might be caused by a different manifest format.
|
||||
return nil, nil
|
||||
logrus.Debugf("could not unmarshal manifest: %v", err)
|
||||
return nil, nil //nolint: nilnil
|
||||
}
|
||||
|
||||
var r []*internal.FileMetadata
|
||||
|
||||
+3
-2
@@ -3,6 +3,7 @@ package chunked
|
||||
import (
|
||||
archivetar "archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -149,7 +150,7 @@ func readEstargzChunkedManifest(blobStream ImageSourceSeekable, blobSize int64,
|
||||
// readZstdChunkedManifest reads the zstd:chunked manifest from the seekable stream blobStream. The blob total size must
|
||||
// be specified.
|
||||
// This function uses the io.github.containers.zstd-chunked. annotations when specified.
|
||||
func readZstdChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) {
|
||||
func readZstdChunkedManifest(ctx context.Context, blobStream ImageSourceSeekable, blobSize int64, annotations map[string]string) ([]byte, int64, error) {
|
||||
footerSize := int64(internal.FooterSizeSupported)
|
||||
if blobSize <= footerSize {
|
||||
return nil, 0, errors.New("blob too small")
|
||||
@@ -244,7 +245,7 @@ func readZstdChunkedManifest(blobStream ImageSourceSeekable, blobSize int64, ann
|
||||
return nil, 0, errors.New("invalid manifest checksum")
|
||||
}
|
||||
|
||||
decoder, err := zstd.NewReader(nil)
|
||||
decoder, err := zstd.NewReader(nil) //nolint:contextcheck
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
+5
-2
@@ -147,7 +147,7 @@ func GetDiffer(ctx context.Context, store storage.Store, blobSize int64, annotat
|
||||
}
|
||||
|
||||
func makeZstdChunkedDiffer(ctx context.Context, store storage.Store, blobSize int64, annotations map[string]string, iss ImageSourceSeekable) (*chunkedDiffer, error) {
|
||||
manifest, tocOffset, err := readZstdChunkedManifest(iss, blobSize, annotations)
|
||||
manifest, tocOffset, err := readZstdChunkedManifest(ctx, iss, blobSize, annotations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read zstd:chunked manifest: %w", err)
|
||||
}
|
||||
@@ -279,6 +279,7 @@ func canDedupFileWithHardLink(file *internal.FileMetadata, fd int, s os.FileInfo
|
||||
func findFileInOSTreeRepos(file *internal.FileMetadata, ostreeRepos []string, dirfd int, useHardLinks bool) (bool, *os.File, int64, error) {
|
||||
digest, err := digest.Parse(file.Digest)
|
||||
if err != nil {
|
||||
logrus.Debugf("could not parse digest: %v", err)
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
payloadLink := digest.Encoded() + ".payload-link"
|
||||
@@ -297,6 +298,7 @@ func findFileInOSTreeRepos(file *internal.FileMetadata, ostreeRepos []string, di
|
||||
}
|
||||
fd, err := unix.Open(sourceFile, unix.O_RDONLY|unix.O_NONBLOCK, 0)
|
||||
if err != nil {
|
||||
logrus.Debugf("could not open sourceFile %s: %v", sourceFile, err)
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
f := os.NewFile(uintptr(fd), "fd")
|
||||
@@ -309,6 +311,7 @@ func findFileInOSTreeRepos(file *internal.FileMetadata, ostreeRepos []string, di
|
||||
|
||||
dstFile, written, err := copyFileContent(fd, file.Name, dirfd, 0, useHardLinks)
|
||||
if err != nil {
|
||||
logrus.Debugf("could not copyFileContent: %v", err)
|
||||
return false, nil, 0, nil
|
||||
}
|
||||
return true, dstFile, written, nil
|
||||
@@ -503,7 +506,7 @@ func openFileUnderRootFallback(dirfd int, name string, flags uint64, mode os.Fil
|
||||
|
||||
hasNoFollow := (flags & unix.O_NOFOLLOW) != 0
|
||||
|
||||
fd := -1
|
||||
var fd int
|
||||
// If O_NOFOLLOW is specified in the flags, then resolve only the parent directory and use the
|
||||
// last component as the path to openat().
|
||||
if hasNoFollow {
|
||||
|
||||
+2
-9
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// Same as DM_DEVICE_* enum values from libdevmapper.h
|
||||
// nolint: deadcode
|
||||
// nolint: unused
|
||||
const (
|
||||
deviceCreate TaskType = iota
|
||||
deviceReload
|
||||
@@ -198,13 +198,6 @@ func (t *Task) setAddNode(addNode AddNodeType) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) setRo() error {
|
||||
if res := DmTaskSetRo(t.unmanaged); res != 1 {
|
||||
return ErrTaskSetRo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) addTarget(start, size uint64, ttype, params string) error {
|
||||
if res := DmTaskAddTarget(t.unmanaged, start, size,
|
||||
ttype, params); res != 1 {
|
||||
@@ -213,7 +206,7 @@ func (t *Task) addTarget(start, size uint64, ttype, params string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) getDeps() (*Deps, error) {
|
||||
func (t *Task) getDeps() (*Deps, error) { //nolint:unused
|
||||
var deps *Deps
|
||||
if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
|
||||
return nil, ErrTaskGetDeps
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ func LogInit(logger DevmapperLogger) {
|
||||
// because we are using callbacks, this function will be called for *every* log
|
||||
// in libdm (even debug ones because there's no way of setting the verbosity
|
||||
// level for an external logging callback).
|
||||
//
|
||||
//export StorageDevmapperLogCallback
|
||||
func StorageDevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
|
||||
msg := C.GoString(message)
|
||||
|
||||
+1
-1
@@ -165,7 +165,7 @@ func (pm *PatternMatcher) Patterns() []*Pattern {
|
||||
return pm.patterns
|
||||
}
|
||||
|
||||
// Pattern defines a single regexp used used to filter file paths.
|
||||
// Pattern defines a single regexp used to filter file paths.
|
||||
type Pattern struct {
|
||||
cleanedPattern string
|
||||
dirs []string
|
||||
|
||||
+2
-2
@@ -34,10 +34,10 @@ func openTree(path string, flags int) (fd int, err error) {
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return int(r), nil
|
||||
return int(r), err
|
||||
}
|
||||
|
||||
// moveMount is a wrapper for the the move_mount syscall.
|
||||
// moveMount is a wrapper for the move_mount syscall.
|
||||
func moveMount(fdTree int, target string) (err error) {
|
||||
var _p0, _p1 *byte
|
||||
|
||||
|
||||
+93
-43
@@ -17,6 +17,20 @@ type AtomicFileWriterOptions struct {
|
||||
// On successful return from Close() this is set to the mtime of the
|
||||
// newly written file.
|
||||
ModTime time.Time
|
||||
// Specifies whether Commit() must be explicitly called to write state
|
||||
// to the destination. This allows an application to preserve the original
|
||||
// file when an error occurs during processing (and not just during write)
|
||||
// The default is false, which will auto-commit on Close
|
||||
ExplicitCommit bool
|
||||
}
|
||||
|
||||
type CommittableWriter interface {
|
||||
io.WriteCloser
|
||||
|
||||
// Commit closes the temporary file associated with this writer, and
|
||||
// provided no errors (during commit or previously during write operations),
|
||||
// will publish the completed file under the intended destination.
|
||||
Commit() error
|
||||
}
|
||||
|
||||
var defaultWriterOptions = AtomicFileWriterOptions{}
|
||||
@@ -27,16 +41,19 @@ func SetDefaultOptions(opts AtomicFileWriterOptions) {
|
||||
defaultWriterOptions = opts
|
||||
}
|
||||
|
||||
// NewAtomicFileWriterWithOpts returns WriteCloser so that writing to it writes to a
|
||||
// temporary file and closing it atomically changes the temporary file to
|
||||
// destination path. Writing and closing concurrently is not allowed.
|
||||
func NewAtomicFileWriterWithOpts(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (io.WriteCloser, error) {
|
||||
// NewAtomicFileWriterWithOpts returns a CommittableWriter so that writing to it
|
||||
// writes to a temporary file, which can later be committed to a destination path,
|
||||
// either by Closing in the case of auto-commit, or manually calling commit if the
|
||||
// ExplicitCommit option is enabled. Writing and closing concurrently is not
|
||||
// allowed.
|
||||
func NewAtomicFileWriterWithOpts(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (CommittableWriter, error) {
|
||||
return newAtomicFileWriter(filename, perm, opts)
|
||||
}
|
||||
|
||||
// newAtomicFileWriter returns WriteCloser so that writing to it writes to a
|
||||
// temporary file and closing it atomically changes the temporary file to
|
||||
// destination path. Writing and closing concurrently is not allowed.
|
||||
// newAtomicFileWriter returns a CommittableWriter so that writing to it writes to
|
||||
// a temporary file, which can later be committed to a destination path, either by
|
||||
// Closing in the case of auto-commit, or manually calling commit if the
|
||||
// ExplicitCommit option is enabled. Writing and closing concurrently is not allowed.
|
||||
func newAtomicFileWriter(filename string, perm os.FileMode, opts *AtomicFileWriterOptions) (*atomicFileWriter, error) {
|
||||
f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
|
||||
if err != nil {
|
||||
@@ -50,17 +67,18 @@ func newAtomicFileWriter(filename string, perm os.FileMode, opts *AtomicFileWrit
|
||||
return nil, err
|
||||
}
|
||||
return &atomicFileWriter{
|
||||
f: f,
|
||||
fn: abspath,
|
||||
perm: perm,
|
||||
noSync: opts.NoSync,
|
||||
f: f,
|
||||
fn: abspath,
|
||||
perm: perm,
|
||||
noSync: opts.NoSync,
|
||||
explicitCommit: opts.ExplicitCommit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a
|
||||
// temporary file and closing it atomically changes the temporary file to
|
||||
// destination path. Writing and closing concurrently is not allowed.
|
||||
func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) {
|
||||
// NewAtomicFileWriterWithOpts returns a CommittableWriter, with auto-commit enabled.
|
||||
// Writing to it writes to a temporary file and closing it atomically changes the
|
||||
// temporary file to destination path. Writing and closing concurrently is not allowed.
|
||||
func NewAtomicFileWriter(filename string, perm os.FileMode) (CommittableWriter, error) {
|
||||
return NewAtomicFileWriterWithOpts(filename, perm, nil)
|
||||
}
|
||||
|
||||
@@ -91,12 +109,14 @@ func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||
}
|
||||
|
||||
type atomicFileWriter struct {
|
||||
f *os.File
|
||||
fn string
|
||||
writeErr error
|
||||
perm os.FileMode
|
||||
noSync bool
|
||||
modTime time.Time
|
||||
f *os.File
|
||||
fn string
|
||||
writeErr error
|
||||
perm os.FileMode
|
||||
noSync bool
|
||||
modTime time.Time
|
||||
closed bool
|
||||
explicitCommit bool
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) Write(dt []byte) (int, error) {
|
||||
@@ -107,43 +127,73 @@ func (w *atomicFileWriter) Write(dt []byte) (int, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) Close() (retErr error) {
|
||||
func (w *atomicFileWriter) closeTempFile() error {
|
||||
if w.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
w.closed = true
|
||||
return w.f.Close()
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) Close() error {
|
||||
return w.complete(!w.explicitCommit)
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) Commit() error {
|
||||
return w.complete(true)
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) complete(commit bool) (retErr error) {
|
||||
if w == nil || w.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
w.closeTempFile()
|
||||
if retErr != nil || w.writeErr != nil {
|
||||
os.Remove(w.f.Name())
|
||||
}
|
||||
}()
|
||||
if !w.noSync {
|
||||
if err := fdatasync(w.f); err != nil {
|
||||
w.f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if commit {
|
||||
return w.commitState()
|
||||
}
|
||||
|
||||
// fstat before closing the fd
|
||||
info, statErr := w.f.Stat()
|
||||
if statErr == nil {
|
||||
w.modTime = info.ModTime()
|
||||
}
|
||||
// We delay error reporting until after the real call to close()
|
||||
// to match the traditional linux close() behaviour that an fd
|
||||
// is invalid (closed) even if close returns failure. While
|
||||
// weird, this allows a well defined way to not leak open fds.
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := w.f.Close(); err != nil {
|
||||
func (w *atomicFileWriter) commitState() error {
|
||||
// Perform a data only sync (fdatasync()) if supported
|
||||
if err := w.postDataWrittenSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if statErr != nil {
|
||||
return statErr
|
||||
}
|
||||
|
||||
if err := os.Chmod(w.f.Name(), w.perm); err != nil {
|
||||
// Capture fstat before closing the fd
|
||||
info, err := w.f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.modTime = info.ModTime()
|
||||
|
||||
if err := w.f.Chmod(w.perm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Perform full sync on platforms that need it
|
||||
if err := w.preRenameSync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Some platforms require closing before rename (Windows)
|
||||
if err := w.closeTempFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if w.writeErr == nil {
|
||||
return os.Rename(w.f.Name(), w.fn)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -195,7 +245,7 @@ func (w syncFileCloser) Close() error {
|
||||
if !defaultWriterOptions.NoSync {
|
||||
return w.File.Close()
|
||||
}
|
||||
err := fdatasync(w.File)
|
||||
err := dataOrFullSync(w.File)
|
||||
if err1 := w.File.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
|
||||
+13
-1
@@ -6,6 +6,18 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func fdatasync(f *os.File) error {
|
||||
func dataOrFullSync(f *os.File) error {
|
||||
return unix.Fdatasync(int(f.Fd()))
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) postDataWrittenSync() error {
|
||||
if w.noSync {
|
||||
return nil
|
||||
}
|
||||
return unix.Fdatasync(int(w.f.Fd()))
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) preRenameSync() error {
|
||||
// On Linux data can be reliably flushed to media without metadata, so defer
|
||||
return nil
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func dataOrFullSync(f *os.File) error {
|
||||
return f.Sync()
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) postDataWrittenSync() error {
|
||||
// many platforms (Mac, Windows) require a full sync to reliably flush to media
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *atomicFileWriter) preRenameSync() error {
|
||||
if w.noSync {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fsync() on Non-linux Unix, FlushFileBuffers (Windows), F_FULLFSYNC (Mac)
|
||||
return w.f.Sync()
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func fdatasync(f *os.File) error {
|
||||
return f.Sync()
|
||||
}
|
||||
+8
-7
@@ -24,13 +24,14 @@ func ParseKeyValueOpt(opt string) (string, string, error) {
|
||||
// input string. It returns a `map[int]bool` with available elements from `val`
|
||||
// set to `true`.
|
||||
// Supported formats:
|
||||
// 7
|
||||
// 1-6
|
||||
// 0,3-4,7,8-10
|
||||
// 0-0,0,1-7
|
||||
// 03,1-3 <- this is gonna get parsed as [1,2,3]
|
||||
// 3,2,1
|
||||
// 0-2,3,1
|
||||
//
|
||||
// 7
|
||||
// 1-6
|
||||
// 0,3-4,7,8-10
|
||||
// 0-0,0,1-7
|
||||
// 03,1-3 <- this is gonna get parsed as [1,2,3]
|
||||
// 3,2,1
|
||||
// 0-2,3,1
|
||||
func ParseUintList(val string) (map[int]bool, error) {
|
||||
if val == "" {
|
||||
return map[int]bool{}, nil
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ func panicIfNotInitialized() {
|
||||
}
|
||||
}
|
||||
|
||||
func naiveSelf() string {
|
||||
func naiveSelf() string { //nolint: unused
|
||||
name := os.Args[0]
|
||||
if filepath.Base(name) == name {
|
||||
if lp, err := exec.LookPath(name); err == nil {
|
||||
|
||||
+3
-3
@@ -7,9 +7,9 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//setCTime will set the create time on a file. On Unix, the create
|
||||
//time is updated as a side effect of setting the modified time, so
|
||||
//no action is required.
|
||||
// setCTime will set the create time on a file. On Unix, the create
|
||||
// time is updated as a side effect of setting the modified time, so
|
||||
// no action is required.
|
||||
func setCTime(path string, ctime time.Time) error {
|
||||
return nil
|
||||
}
|
||||
+2
-2
@@ -9,8 +9,8 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//setCTime will set the create time on a file. On Windows, this requires
|
||||
//calling SetFileTime and explicitly including the create time.
|
||||
// setCTime will set the create time on a file. On Windows, this requires
|
||||
// calling SetFileTime and explicitly including the create time.
|
||||
func setCTime(path string, ctime time.Time) error {
|
||||
ctimespec := windows.NsecToTimespec(ctime.UnixNano())
|
||||
pathp, e := windows.UTF16PtrFromString(path)
|
||||
|
||||
+2
-1
@@ -59,7 +59,8 @@ func getSwapInfo() (int64, int64, error) {
|
||||
}
|
||||
|
||||
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||
// MemInfo type.
|
||||
//
|
||||
// MemInfo type.
|
||||
func ReadMemInfo() (*MemInfo, error) {
|
||||
MemTotal, MemFree, err := getMemInfo()
|
||||
if err != nil {
|
||||
|
||||
+2
-1
@@ -81,7 +81,8 @@ func getFreeMem() int64 {
|
||||
}
|
||||
|
||||
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||
// MemInfo type.
|
||||
//
|
||||
// MemInfo type.
|
||||
func ReadMemInfo() (*MemInfo, error) {
|
||||
|
||||
ppKernel := C.getPpKernel()
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@ type memorystatusex struct {
|
||||
}
|
||||
|
||||
// ReadMemInfo retrieves memory statistics of the host system and returns a
|
||||
// MemInfo type.
|
||||
//
|
||||
// MemInfo type.
|
||||
func ReadMemInfo() (*MemInfo, error) {
|
||||
msi := &memorystatusex{
|
||||
dwLength: 64,
|
||||
|
||||
+4
-5
@@ -33,9 +33,9 @@ type Cmd struct {
|
||||
*exec.Cmd
|
||||
UnshareFlags int
|
||||
UseNewuidmap bool
|
||||
UidMappings []specs.LinuxIDMapping // nolint: golint
|
||||
UidMappings []specs.LinuxIDMapping // nolint: revive,golint
|
||||
UseNewgidmap bool
|
||||
GidMappings []specs.LinuxIDMapping // nolint: golint
|
||||
GidMappings []specs.LinuxIDMapping // nolint: revive,golint
|
||||
GidMappingsEnableSetgroups bool
|
||||
Setsid bool
|
||||
Setpgrp bool
|
||||
@@ -175,12 +175,11 @@ func (c *Cmd) Start() error {
|
||||
pidWrite = nil
|
||||
|
||||
// Read the child's PID from the pipe.
|
||||
pidString := ""
|
||||
b := new(bytes.Buffer)
|
||||
if _, err := io.Copy(b, pidRead); err != nil {
|
||||
return fmt.Errorf("reading child PID: %w", err)
|
||||
}
|
||||
pidString = b.String()
|
||||
pidString := b.String()
|
||||
pid, err := strconv.Atoi(pidString)
|
||||
if err != nil {
|
||||
fmt.Fprintf(continueWrite, "error parsing PID %q: %v", pidString, err)
|
||||
@@ -451,7 +450,7 @@ type Runnable interface {
|
||||
Run() error
|
||||
}
|
||||
|
||||
func bailOnError(err error, format string, a ...interface{}) { // nolint: golint,goprintffuncname
|
||||
func bailOnError(err error, format string, a ...interface{}) { // nolint: revive,goprintffuncname
|
||||
if err != nil {
|
||||
if format != "" {
|
||||
logrus.Errorf("%s: %v", fmt.Sprintf(format, a...), err)
|
||||
|
||||
+2
@@ -34,6 +34,8 @@ graphroot = "/var/lib/containers/storage"
|
||||
|
||||
# Transient store mode makes all container metadata be saved in temporary storage
|
||||
# (i.e. runroot above). This is faster, but doesn't persist across reboots.
|
||||
# Additional garbage collection must also be performed at boot-time, so this
|
||||
# option should remain disabled in most configurations.
|
||||
# transient_store = true
|
||||
|
||||
[storage.options]
|
||||
|
||||
+269
-65
@@ -506,10 +506,13 @@ type Store interface {
|
||||
// GetDigestLock returns digest-specific Locker.
|
||||
GetDigestLock(digest.Digest) (Locker, error)
|
||||
|
||||
// LayerFromAdditionalLayerStore searches layers from the additional layer store and
|
||||
// returns the object for handling this. Note that this hasn't been stored to this store
|
||||
// yet so this needs to be done through PutAs method.
|
||||
// Releasing AdditionalLayer handler is caller's responsibility.
|
||||
// LayerFromAdditionalLayerStore searches the additional layer store and returns an object
|
||||
// which can create a layer with the specified digest associated with the specified image
|
||||
// reference. Note that this hasn't been stored to this store yet: the actual creation of
|
||||
// a usable layer is done by calling the returned object's PutAs() method. After creating
|
||||
// a layer, the caller must then call the object's Release() method to free any temporary
|
||||
// resources which were allocated for the object by this method or the object's PutAs()
|
||||
// method.
|
||||
// This API is experimental and can be changed without bumping the major version number.
|
||||
LookupAdditionalLayer(d digest.Digest, imageref string) (AdditionalLayer, error)
|
||||
|
||||
@@ -562,6 +565,17 @@ type LayerOptions struct {
|
||||
UncompressedDigest digest.Digest
|
||||
// True is the layer info can be treated as volatile
|
||||
Volatile bool
|
||||
// BigData is a set of items which should be stored with the layer.
|
||||
BigData []LayerBigDataOption
|
||||
// Flags is a set of named flags and their values to store with the layer.
|
||||
// Currently these can only be set when the layer record is created, but that
|
||||
// could change in the future.
|
||||
Flags map[string]interface{}
|
||||
}
|
||||
|
||||
type LayerBigDataOption struct {
|
||||
Key string
|
||||
Data io.Reader
|
||||
}
|
||||
|
||||
// ImageOptions is used for passing options to a Store's CreateImage() method.
|
||||
@@ -571,6 +585,26 @@ type ImageOptions struct {
|
||||
CreationDate time.Time
|
||||
// Digest is a hard-coded digest value that we can use to look up the image. It is optional.
|
||||
Digest digest.Digest
|
||||
// Digests is a list of digest values of the image's manifests, and
|
||||
// possibly a manually-specified value, that we can use to locate the
|
||||
// image. If Digest is set, its value is also in this list.
|
||||
Digests []digest.Digest
|
||||
// Metadata is caller-specified metadata associated with the layer.
|
||||
Metadata string
|
||||
// BigData is a set of items which should be stored with the image.
|
||||
BigData []ImageBigDataOption
|
||||
// NamesHistory is used for guessing for what this image was named when a container was created based
|
||||
// on it, but it no longer has any names.
|
||||
NamesHistory []string
|
||||
// Flags is a set of named flags and their values to store with the image. Currently these can only
|
||||
// be set when the image record is created, but that could change in the future.
|
||||
Flags map[string]interface{}
|
||||
}
|
||||
|
||||
type ImageBigDataOption struct {
|
||||
Key string
|
||||
Data []byte
|
||||
Digest digest.Digest
|
||||
}
|
||||
|
||||
// ContainerOptions is used for passing options to a Store's CreateContainer() method.
|
||||
@@ -580,11 +614,23 @@ type ContainerOptions struct {
|
||||
// container's layer will inherit settings from the image's top layer
|
||||
// or, if it is not being created based on an image, the Store object.
|
||||
types.IDMappingOptions
|
||||
LabelOpts []string
|
||||
LabelOpts []string
|
||||
// Flags is a set of named flags and their values to store with the container.
|
||||
// Currently these can only be set when the container record is created, but that
|
||||
// could change in the future.
|
||||
Flags map[string]interface{}
|
||||
MountOpts []string
|
||||
Volatile bool
|
||||
StorageOpt map[string]string
|
||||
// Metadata is caller-specified metadata associated with the container.
|
||||
Metadata string
|
||||
// BigData is a set of items which should be stored for the container.
|
||||
BigData []ContainerBigDataOption
|
||||
}
|
||||
|
||||
type ContainerBigDataOption struct {
|
||||
Key string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type store struct {
|
||||
@@ -1221,7 +1267,7 @@ func canUseShifting(store rwLayerStore, uidmap, gidmap []idtools.IDMap) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *store) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions, diff io.Reader) (*Layer, int64, error) {
|
||||
func (s *store) PutLayer(id, parent string, names []string, mountLabel string, writeable bool, lOptions *LayerOptions, diff io.Reader) (*Layer, int64, error) {
|
||||
var parentLayer *Layer
|
||||
rlstore, rlstores, err := s.bothLayerStoreKinds()
|
||||
if err != nil {
|
||||
@@ -1235,8 +1281,11 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
|
||||
return nil, -1, err
|
||||
}
|
||||
defer s.containerStore.stopWriting()
|
||||
if options == nil {
|
||||
options = &LayerOptions{}
|
||||
var options LayerOptions
|
||||
if lOptions != nil {
|
||||
options = *lOptions
|
||||
options.BigData = copyLayerBigDataOptionSlice(lOptions.BigData)
|
||||
options.Flags = copyStringInterfaceMap(lOptions.Flags)
|
||||
}
|
||||
if options.HostUIDMapping {
|
||||
options.UIDMap = nil
|
||||
@@ -1303,7 +1352,7 @@ func (s *store) PutLayer(id, parent string, names []string, mountLabel string, w
|
||||
GIDMap: copyIDMap(gidMap),
|
||||
}
|
||||
}
|
||||
return rlstore.Put(id, parentLayer, names, mountLabel, nil, &layerOptions, writeable, nil, diff)
|
||||
return rlstore.create(id, parentLayer, names, mountLabel, nil, &layerOptions, writeable, diff)
|
||||
}
|
||||
|
||||
func (s *store) CreateLayer(id, parent string, names []string, mountLabel string, writeable bool, options *LayerOptions) (*Layer, error) {
|
||||
@@ -1311,7 +1360,7 @@ func (s *store) CreateLayer(id, parent string, names []string, mountLabel string
|
||||
return layer, err
|
||||
}
|
||||
|
||||
func (s *store) CreateImage(id string, names []string, layer, metadata string, options *ImageOptions) (*Image, error) {
|
||||
func (s *store) CreateImage(id string, names []string, layer, metadata string, iOptions *ImageOptions) (*Image, error) {
|
||||
if layer != "" {
|
||||
layerStores, err := s.allLayerStores()
|
||||
if err != nil {
|
||||
@@ -1335,17 +1384,90 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
|
||||
layer = ilayer.ID
|
||||
}
|
||||
|
||||
var res *Image
|
||||
err := s.writeToImageStore(func() error {
|
||||
creationDate := time.Now().UTC()
|
||||
if options != nil && !options.CreationDate.IsZero() {
|
||||
creationDate = options.CreationDate
|
||||
}
|
||||
var options ImageOptions
|
||||
var namesToAddAfterCreating []string
|
||||
|
||||
var err error
|
||||
res, err = s.imageStore.Create(id, names, layer, metadata, creationDate, options.Digest)
|
||||
return err
|
||||
})
|
||||
if err := s.imageStore.startWriting(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.imageStore.stopWriting()
|
||||
|
||||
// Check if the ID refers to an image in a read-only store -- we want
|
||||
// to allow images in read-only stores to have their names changed, so
|
||||
// if we find one, merge the new values in with what we know about the
|
||||
// image that's already there.
|
||||
if id != "" {
|
||||
for _, is := range s.roImageStores {
|
||||
store := is
|
||||
if err := store.startReading(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer store.stopReading()
|
||||
if i, err := store.Get(id); err == nil {
|
||||
// set information about this image in "options"
|
||||
options = ImageOptions{
|
||||
Metadata: i.Metadata,
|
||||
CreationDate: i.Created,
|
||||
Digest: i.Digest,
|
||||
Digests: copyDigestSlice(i.Digests),
|
||||
NamesHistory: copyStringSlice(i.NamesHistory),
|
||||
}
|
||||
for _, key := range i.BigDataNames {
|
||||
data, err := store.BigData(id, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataDigest, err := store.BigDataDigest(id, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.BigData = append(options.BigData, ImageBigDataOption{
|
||||
Key: key,
|
||||
Data: data,
|
||||
Digest: dataDigest,
|
||||
})
|
||||
}
|
||||
namesToAddAfterCreating = dedupeStrings(append(append([]string{}, i.Names...), names...))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge any passed-in options into "options" as best we can
|
||||
if iOptions != nil {
|
||||
if !iOptions.CreationDate.IsZero() {
|
||||
options.CreationDate = iOptions.CreationDate
|
||||
}
|
||||
if iOptions.Digest != "" {
|
||||
options.Digest = iOptions.Digest
|
||||
}
|
||||
options.Digests = append(options.Digests, copyDigestSlice(iOptions.Digests)...)
|
||||
if iOptions.Metadata != "" {
|
||||
options.Metadata = iOptions.Metadata
|
||||
}
|
||||
options.BigData = append(options.BigData, copyImageBigDataOptionSlice(iOptions.BigData)...)
|
||||
options.NamesHistory = append(options.NamesHistory, copyStringSlice(iOptions.NamesHistory)...)
|
||||
if options.Flags == nil {
|
||||
options.Flags = make(map[string]interface{})
|
||||
}
|
||||
for k, v := range iOptions.Flags {
|
||||
options.Flags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if options.CreationDate.IsZero() {
|
||||
options.CreationDate = time.Now().UTC()
|
||||
}
|
||||
if metadata != "" {
|
||||
options.Metadata = metadata
|
||||
}
|
||||
|
||||
res, err := s.imageStore.create(id, names, layer, options)
|
||||
if err == nil && len(namesToAddAfterCreating) > 0 {
|
||||
// set any names we pulled up from an additional image store, now that we won't be
|
||||
// triggering a duplicate names error
|
||||
err = s.imageStore.updateNames(res.ID, namesToAddAfterCreating, addNames)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
@@ -1426,26 +1548,22 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore roImageStore, rlst
|
||||
// mappings, and register it as an alternate top layer in the image.
|
||||
var layerOptions LayerOptions
|
||||
if canUseShifting(rlstore, options.UIDMap, options.GIDMap) {
|
||||
layerOptions = LayerOptions{
|
||||
IDMappingOptions: types.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
UIDMap: nil,
|
||||
GIDMap: nil,
|
||||
},
|
||||
layerOptions.IDMappingOptions = types.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
UIDMap: nil,
|
||||
GIDMap: nil,
|
||||
}
|
||||
} else {
|
||||
layerOptions = LayerOptions{
|
||||
IDMappingOptions: types.IDMappingOptions{
|
||||
HostUIDMapping: options.HostUIDMapping,
|
||||
HostGIDMapping: options.HostGIDMapping,
|
||||
UIDMap: copyIDMap(options.UIDMap),
|
||||
GIDMap: copyIDMap(options.GIDMap),
|
||||
},
|
||||
layerOptions.IDMappingOptions = types.IDMappingOptions{
|
||||
HostUIDMapping: options.HostUIDMapping,
|
||||
HostGIDMapping: options.HostGIDMapping,
|
||||
UIDMap: copyIDMap(options.UIDMap),
|
||||
GIDMap: copyIDMap(options.GIDMap),
|
||||
}
|
||||
}
|
||||
layerOptions.TemplateLayer = layer.ID
|
||||
mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, nil)
|
||||
mappedLayer, _, err := rlstore.create("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating an ID-mapped copy of layer %q: %w", layer.ID, err)
|
||||
}
|
||||
@@ -1459,9 +1577,17 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore roImageStore, rlst
|
||||
return mappedLayer, nil
|
||||
}
|
||||
|
||||
func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error) {
|
||||
if options == nil {
|
||||
options = &ContainerOptions{}
|
||||
func (s *store) CreateContainer(id string, names []string, image, layer, metadata string, cOptions *ContainerOptions) (*Container, error) {
|
||||
var options ContainerOptions
|
||||
if cOptions != nil {
|
||||
options = *cOptions
|
||||
options.IDMappingOptions.UIDMap = copyIDMap(cOptions.IDMappingOptions.UIDMap)
|
||||
options.IDMappingOptions.GIDMap = copyIDMap(cOptions.IDMappingOptions.GIDMap)
|
||||
options.LabelOpts = copyStringSlice(cOptions.LabelOpts)
|
||||
options.Flags = copyStringInterfaceMap(cOptions.Flags)
|
||||
options.MountOpts = copyStringSlice(cOptions.MountOpts)
|
||||
options.StorageOpt = copyStringStringMap(cOptions.StorageOpt)
|
||||
options.BigData = copyContainerBigDataOptionSlice(cOptions.BigData)
|
||||
}
|
||||
if options.HostUIDMapping {
|
||||
options.UIDMap = nil
|
||||
@@ -1469,6 +1595,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
|
||||
if options.HostGIDMapping {
|
||||
options.GIDMap = nil
|
||||
}
|
||||
options.Metadata = metadata
|
||||
rlstore, lstores, err := s.bothLayerStoreKinds() // lstores will be locked read-only if image != ""
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1574,22 +1701,19 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
|
||||
Volatile: options.Volatile || s.transientStore,
|
||||
}
|
||||
if canUseShifting(rlstore, uidMap, gidMap) {
|
||||
layerOptions.IDMappingOptions =
|
||||
types.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
UIDMap: nil,
|
||||
GIDMap: nil,
|
||||
}
|
||||
layerOptions.IDMappingOptions = types.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
UIDMap: nil,
|
||||
GIDMap: nil,
|
||||
}
|
||||
} else {
|
||||
layerOptions.IDMappingOptions =
|
||||
types.IDMappingOptions{
|
||||
HostUIDMapping: idMappingsOptions.HostUIDMapping,
|
||||
HostGIDMapping: idMappingsOptions.HostGIDMapping,
|
||||
UIDMap: copyIDMap(uidMap),
|
||||
GIDMap: copyIDMap(gidMap),
|
||||
}
|
||||
|
||||
layerOptions.IDMappingOptions = types.IDMappingOptions{
|
||||
HostUIDMapping: idMappingsOptions.HostUIDMapping,
|
||||
HostGIDMapping: idMappingsOptions.HostGIDMapping,
|
||||
UIDMap: copyIDMap(uidMap),
|
||||
GIDMap: copyIDMap(gidMap),
|
||||
}
|
||||
}
|
||||
if options.Flags == nil {
|
||||
options.Flags = make(map[string]interface{})
|
||||
@@ -1610,7 +1734,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
|
||||
options.Flags[mountLabelFlag] = mountLabel
|
||||
}
|
||||
|
||||
clayer, err := rlstore.Create(layer, imageTopLayer, nil, mlabel, options.StorageOpt, layerOptions, true)
|
||||
clayer, _, err := rlstore.create(layer, imageTopLayer, nil, mlabel, options.StorageOpt, layerOptions, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1630,7 +1754,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
|
||||
GIDMap: copyIDMap(options.GIDMap),
|
||||
}
|
||||
var err error
|
||||
container, err = s.containerStore.Create(id, names, imageID, layer, metadata, options)
|
||||
container, err = s.containerStore.create(id, names, imageID, layer, &options)
|
||||
if err != nil || container == nil {
|
||||
if err2 := rlstore.Delete(layer); err2 != nil {
|
||||
if err == nil {
|
||||
@@ -2070,18 +2194,30 @@ func (s *store) Exists(id string) bool {
|
||||
return s.containerStore.Exists(id)
|
||||
}
|
||||
|
||||
func dedupeNames(names []string) []string {
|
||||
seen := make(map[string]bool)
|
||||
func dedupeStrings(names []string) []string {
|
||||
seen := make(map[string]struct{})
|
||||
deduped := make([]string, 0, len(names))
|
||||
for _, name := range names {
|
||||
if _, wasSeen := seen[name]; !wasSeen {
|
||||
seen[name] = true
|
||||
seen[name] = struct{}{}
|
||||
deduped = append(deduped, name)
|
||||
}
|
||||
}
|
||||
return deduped
|
||||
}
|
||||
|
||||
func dedupeDigests(digests []digest.Digest) []digest.Digest {
|
||||
seen := make(map[digest.Digest]struct{})
|
||||
deduped := make([]digest.Digest, 0, len(digests))
|
||||
for _, d := range digests {
|
||||
if _, wasSeen := seen[d]; !wasSeen {
|
||||
seen[d] = struct{}{}
|
||||
deduped = append(deduped, d)
|
||||
}
|
||||
}
|
||||
return deduped
|
||||
}
|
||||
|
||||
// Deprecated: Prone to race conditions, suggested alternatives are `AddNames` and `RemoveNames`.
|
||||
func (s *store) SetNames(id string, names []string) error {
|
||||
return s.updateNames(id, names, setNames)
|
||||
@@ -2096,7 +2232,7 @@ func (s *store) RemoveNames(id string, names []string) error {
|
||||
}
|
||||
|
||||
func (s *store) updateNames(id string, names []string, op updateNameOperation) error {
|
||||
deduped := dedupeNames(names)
|
||||
deduped := dedupeStrings(names)
|
||||
|
||||
layerFound := false
|
||||
if err := s.writeToLayerStore(func(rlstore rwLayerStore) error {
|
||||
@@ -2117,7 +2253,8 @@ func (s *store) updateNames(id string, names []string, op updateNameOperation) e
|
||||
return s.imageStore.updateNames(id, deduped, op)
|
||||
}
|
||||
|
||||
// Check is id refers to a RO Store
|
||||
// Check if the id refers to a read-only image store -- we want to allow images in
|
||||
// read-only stores to have their names changed.
|
||||
for _, is := range s.roImageStores {
|
||||
store := is
|
||||
if err := store.startReading(); err != nil {
|
||||
@@ -2125,12 +2262,36 @@ func (s *store) updateNames(id string, names []string, op updateNameOperation) e
|
||||
}
|
||||
defer store.stopReading()
|
||||
if i, err := store.Get(id); err == nil {
|
||||
if len(deduped) > 1 {
|
||||
// Do not want to create image name in R/W storage
|
||||
deduped = deduped[1:]
|
||||
// "pull up" the image so that we can change its names list
|
||||
options := ImageOptions{
|
||||
CreationDate: i.Created,
|
||||
Digest: i.Digest,
|
||||
Digests: copyDigestSlice(i.Digests),
|
||||
Metadata: i.Metadata,
|
||||
NamesHistory: copyStringSlice(i.NamesHistory),
|
||||
Flags: copyStringInterfaceMap(i.Flags),
|
||||
}
|
||||
_, err := s.imageStore.Create(id, deduped, i.TopLayer, i.Metadata, i.Created, i.Digest)
|
||||
return err
|
||||
for _, key := range i.BigDataNames {
|
||||
data, err := store.BigData(id, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataDigest, err := store.BigDataDigest(id, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.BigData = append(options.BigData, ImageBigDataOption{
|
||||
Key: key,
|
||||
Data: data,
|
||||
Digest: dataDigest,
|
||||
})
|
||||
}
|
||||
_, err = s.imageStore.create(id, i.Names, i.TopLayer, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// now make the changes to the writeable image record's names list
|
||||
return s.imageStore.updateNames(id, deduped, op)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2962,6 +3123,16 @@ func (s *store) Image(id string) (*Image, error) {
|
||||
if done, err := s.readAllImageStores(func(store roImageStore) (bool, error) {
|
||||
image, err := store.Get(id)
|
||||
if err == nil {
|
||||
if store != s.imageStore {
|
||||
// found it in a read-only store - readAllImageStores() still has the writeable store locked for reading
|
||||
if _, localErr := s.imageStore.Get(image.ID); localErr == nil {
|
||||
// if the lookup key was a name, and we found the image in a read-only
|
||||
// store, but we have an entry with the same ID in the read-write store,
|
||||
// then the name was removed when we duplicated the image's
|
||||
// record into writable storage, so we should ignore this entry
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
res = image
|
||||
return true, nil
|
||||
}
|
||||
@@ -3247,6 +3418,14 @@ func copyStringDigestMap(m map[string]digest.Digest) map[string]digest.Digest {
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyStringStringMap(m map[string]string) map[string]string {
|
||||
ret := make(map[string]string, len(m))
|
||||
for k, v := range m {
|
||||
ret[k] = v
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyDigestSlice(slice []digest.Digest) []digest.Digest {
|
||||
if len(slice) == 0 {
|
||||
return nil
|
||||
@@ -3266,6 +3445,31 @@ func copyStringInterfaceMap(m map[string]interface{}) map[string]interface{} {
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyLayerBigDataOptionSlice(slice []LayerBigDataOption) []LayerBigDataOption {
|
||||
ret := make([]LayerBigDataOption, len(slice))
|
||||
copy(ret, slice)
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyImageBigDataOptionSlice(slice []ImageBigDataOption) []ImageBigDataOption {
|
||||
ret := make([]ImageBigDataOption, len(slice))
|
||||
for i := range slice {
|
||||
ret[i].Key = slice[i].Key
|
||||
ret[i].Data = append([]byte{}, slice[i].Data...)
|
||||
ret[i].Digest = slice[i].Digest
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func copyContainerBigDataOptionSlice(slice []ContainerBigDataOption) []ContainerBigDataOption {
|
||||
ret := make([]ContainerBigDataOption, len(slice))
|
||||
for i := range slice {
|
||||
ret[i].Key = slice[i].Key
|
||||
ret[i].Data = append([]byte{}, slice[i].Data...)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// AutoUserNsMinSize is the minimum size for automatically created user namespaces
|
||||
const AutoUserNsMinSize = 1024
|
||||
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ outer:
|
||||
|
||||
// We need to create a temporary layer so we can mount it and lookup the
|
||||
// maximum IDs used.
|
||||
clayer, err := rlstore.Create("", topLayer, nil, "", nil, layerOptions, false)
|
||||
clayer, _, err := rlstore.create("", topLayer, nil, "", nil, layerOptions, false, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
+1
-14
@@ -2,7 +2,6 @@ package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
|
||||
"github.com/containers/storage/types"
|
||||
)
|
||||
@@ -71,17 +70,5 @@ func applyNameOperation(oldNames []string, opParameters []string, op updateNameO
|
||||
default:
|
||||
return result, errInvalidUpdateNameOperation
|
||||
}
|
||||
return dedupeNames(result), nil
|
||||
}
|
||||
|
||||
func nameLooksLikeID(name string) bool {
|
||||
if len(name) != 64 {
|
||||
return false
|
||||
}
|
||||
for _, c := range name {
|
||||
if !unicode.Is(unicode.ASCII_Hex_Digit, c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return dedupeStrings(result), nil
|
||||
}
|
||||
Reference in new issue
Block a user