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:
Aditya R committed 2023-04-10 17:30:14 +05:30
1 parent c04ccdbc55
commit 260bc3ec4c
274 files changed
+19599 -1689

No files matched your search

+13 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 doesnt reject duplicate extensions, and doesnt 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 doesnt 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
View File
@@ -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 cosigns 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1 +1 @@
1.45.5-dev
1.46.1
+38 -15
View File
@@ -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 shouldnt 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 shouldnt 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 shouldnt 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 shouldnt 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
View File
@@ -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 shouldnt 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 shouldnt 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 shouldnt 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 shouldnt
// 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 shouldnt happen — and in that case the index is already in the desired state anyway.
// The stores 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 shouldnt
// happen — and in that case the index is already in the desired state
// anyway. The stores 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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()
}
@@ -1,12 +0,0 @@
//go:build !linux
// +build !linux
package ioutils
import (
"os"
)
func fdatasync(f *os.File) error {
return f.Sync()
}
+8 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}