diff options
47 files changed, 496 insertions, 174 deletions
@@ -357,6 +357,7 @@ remotesystem: .PHONY: localapiv2 localapiv2: env PODMAN=./bin/podman ./test/apiv2/test-apiv2 + env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/rest_api/ .PHONY: remoteapiv2 remoteapiv2: @@ -7,6 +7,7 @@ approvers: - rhatdan - TomSweeneyRedHat - vrothberg + - umohnani8 reviewers: - baude - edsantiago @@ -18,3 +19,4 @@ reviewers: - vrothberg - ashley-cui - QiWang19 + - umohnani8 diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index bd3e5fafd..ca1e25be1 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -25,11 +25,8 @@ func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false - const cpuPeriod = 100000 - if c.CPUS > 0 { - quota := int64(c.CPUS * cpuPeriod) - period := uint64(cpuPeriod) + period, quota := util.CoresToPeriodAndQuota(c.CPUS) cpu.Period = &period cpu.Quota = "a diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index 8ab20ccae..b3c160ddf 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -313,7 +313,7 @@ func getBindMount(args []string) (spec.Mount, error) { } setExec = true newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z": + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": newMount.Options = append(newMount.Options, kv[0]) case "bind-propagation": if len(kv) == 1 { diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go index 0ff482cd9..f48d1cd94 100644 --- a/cmd/podman/images/build.go +++ b/cmd/podman/images/build.go @@ -240,13 +240,9 @@ func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buil } } - pullPolicy := imagebuildah.PullIfNewer - if c.Flags().Changed("pull") { - if flags.Pull { - pullPolicy = imagebuildah.PullAlways - } else { - pullPolicy = imagebuildah.PullNever - } + pullPolicy := imagebuildah.PullIfMissing + if c.Flags().Changed("pull") && flags.Pull { + pullPolicy = imagebuildah.PullAlways } if flags.PullAlways { pullPolicy = imagebuildah.PullAlways diff --git a/docs/source/markdown/containers-mounts.conf.5.md b/docs/source/markdown/containers-mounts.conf.5.md index 130c1c523..74492c831 100644 --- a/docs/source/markdown/containers-mounts.conf.5.md +++ b/docs/source/markdown/containers-mounts.conf.5.md @@ -10,7 +10,7 @@ The mounts.conf file specifies volume mount directories that are automatically m The format of the mounts.conf is the volume format `/SRC:/DEST`, one mount per line. For example, a mounts.conf with the line `/usr/share/secrets:/run/secrets` would cause the contents of the `/usr/share/secrets` directory on the host to be mounted on the `/run/secrets` directory inside the container. Setting mountpoints allows containers to use the files of the host, for instance, to use the host's subscription to some enterprise Linux distribution. ## FILES -Some distributions may provide a `/usr/share/containers/mounts.conf` file to provide default mounts, but users can create a `/etc/containers/mounts.conf`, to specify their own special volumes to mount in the container. +Some distributions may provide a `/usr/share/containers/mounts.conf` file to provide default mounts, but users can create a `/etc/containers/mounts.conf`, to specify their own special volumes to mount in the container. When Podman runs in rootless mode, the file `$HOME/.config/containers/mounts.conf` will override the default if it exists. ## HISTORY Aug 2018, Originally compiled by Valentin Rothberg <vrothberg@suse.com> diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index f1fddb2fc..1e1e1d27e 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -384,16 +384,13 @@ not required for Buildah as it supports only Linux. **--pull** -When the flag is enabled, attempt to pull the latest image from the registries -listed in registries.conf if a local image does not exist or the image is newer -than the one in storage. Raise an error if the image is not in any listed -registry and is not present locally. +When the option is specified or set to "true", pull the image from the first registry +it is found in as listed in registries.conf. Raise an error if not found in the +registries, even if the image is present locally. -If the flag is disabled (with *--pull=false*), do not pull the image from the -registry, unless there is no local image. Raise an error if the image is not -in any registry and is not present locally. - -Defaults to *true*. +If the option is disabled (with *--pull=false*), or not specified, pull the image +from the registry only if the image is not present locally. Raise an error if the image +is not found in the registries. **--pull-always** diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index f823ac565..e243a5842 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -541,7 +541,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · ro, readonly: true or false (default). - · bind-propagation: shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2). + · bind-propagation: shared, slave, private, unbindable, rshared, rslave, runbindable, or rprivate(default). See also mount(2). . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. @@ -962,7 +962,7 @@ The _options_ is a comma delimited list and can be: * **rw**|**ro** * **z**|**Z** -* [**r**]**shared**|[**r**]**slave**|[**r**]**private** +* [**r**]**shared**|[**r**]**slave**|[**r**]**private**[**r**]**unbindable** * [**r**]**bind** * [**no**]**exec** * [**no**]**dev** @@ -1048,13 +1048,14 @@ visible on host and vice versa. Making a volume `slave` enables only one way mount propagation and that is mounts done on host under that volume will be visible inside container but not the other way around. <sup>[[1]](#Footnote1)</sup> -To control mount propagation property of volume one can use `:[r]shared`, -`:[r]slave` or `:[r]private` propagation flag. Propagation property can -be specified only for bind mounted volumes and not for internal volumes or -named volumes. For mount propagation to work source mount point (mount point -where source dir is mounted on) has to have right propagation properties. For -shared volumes, source mount point has to be shared. And for slave volumes, -source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup> +To control mount propagation property of a volume one can use the [**r**]**shared**, +[**r**]**slave**, [**r**]**private** or the [**r**]**unbindable** propagation flag. +Propagation property can be specified only for bind mounted volumes and not for +internal volumes or named volumes. For mount propagation to work the source mount +point (the mount point where source dir is mounted on) has to have the right propagation +properties. For shared volumes, the source mount point has to be shared. And for +slave volumes, the source mount point has to be either shared or slave. +<sup>[[1]](#Footnote1)</sup> If you want to recursively mount a volume and all of its submounts into a container, then you can use the `rbind` option. By default the bind option is diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 71f77d307..0166a344a 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -567,7 +567,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · ro, readonly: true or false (default). - · bind-propagation: shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2). + · bind-propagation: shared, slave, private, unbindable, rshared, rslave, runbindable, or rprivate(default). See also mount(2). . bind-nonrecursive: do not setup a recursive bind mount. By default it is recursive. @@ -1015,7 +1015,7 @@ The _options_ is a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup> * **rw**|**ro** * **z**|**Z** -* [**r**]**shared**|[**r**]**slave**|[**r**]**private** +* [**r**]**shared**|[**r**]**slave**|[**r**]**private**[**r**]**unbindable** * [**r**]**bind** * [**no**]**exec** * [**no**]**dev** @@ -1099,12 +1099,13 @@ way mount propagation and that is mounts done on host under that volume will be visible inside container but not the other way around. <sup>[[1]](#Footnote1)</sup> To control mount propagation property of volume one can use [**r**]**shared**, -[**r**]**slave** or [**r**]**private** propagation flag. Propagation property can -be specified only for bind mounted volumes and not for internal volumes or -named volumes. For mount propagation to work source mount point (mount point -where source dir is mounted on) has to have right propagation properties. For -shared volumes, source mount point has to be shared. And for slave volumes, -source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup> +[**r**]**slave**, [**r**]**private** or [**r**]**unbindable** propagation flag. +Propagation property can be specified only for bind mounted volumes and not for +internal volumes or named volumes. For mount propagation to work source mount +point (mount point where source dir is mounted on) has to have right propagation +properties. For shared volumes, source mount point has to be shared. And for +slave volumes, source mount has to be either shared or slave. +<sup>[[1]](#Footnote1)</sup> If you want to recursively mount a volume and all of its submounts into a container, then you can use the **rbind** option. By default the bind option is diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 7f7b4853d..246d235ee 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -12,7 +12,7 @@ Learn how to setup Podman and perform some basic commands with the utility. The steps required to setup rootless Podman are enumerated. -**[Setup Mac/Windows](mac_win_client.md) +**[Setup Mac/Windows](mac_win_client.md)** Special setup for running the Podman remote client on a Mac or Windows PC and connecting to Podman running on a Linux VM are documented. @@ -15,7 +15,7 @@ require ( github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.7.0 github.com/containers/psgo v1.5.1 - github.com/containers/storage v1.23.7 + github.com/containers/storage v1.23.8 github.com/coreos/go-systemd/v22 v22.1.0 github.com/cri-o/ocicni v0.2.0 github.com/cyphar/filepath-securejoin v0.2.2 @@ -101,6 +101,8 @@ github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzP github.com/containers/storage v1.23.6/go.mod h1:haFs0HRowKwyzvWEx9EgI3WsL8XCSnBDb5f8P5CAxJY= github.com/containers/storage v1.23.7 h1:43ImvG/npvQSZXRjaudVvKISIuZSfI6qvtSNQQSGO/A= github.com/containers/storage v1.23.7/go.mod h1:cUT2zHjtx+WlVri30obWmM2gpqpi8jfPsmIzP1TVpEI= +github.com/containers/storage v1.23.8 h1:Z3KKE9BkbW6CGOjIeTtvX+Dl9pFX8QgvSD2j/tS+r5E= +github.com/containers/storage v1.23.8/go.mod h1:3b2ktpB6pw53SEeIoFfO0sQfP9+IoJJKPq5iJk74gxE= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= @@ -305,6 +307,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.1 h1:bPb7nMRdOZYDrpPMTA3EInUQrdgoBinqUuSwlGdKDdE= github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 162f70326..f78d74ef7 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -270,7 +270,7 @@ func parseMountOptionsForInspect(options []string, mount *define.InspectMount) { isRW = false case "rw": // Do nothing, silently discard - case "shared", "slave", "private", "rshared", "rslave", "rprivate": + case "shared", "slave", "private", "rshared", "rslave", "rprivate", "unbindable", "runbindable": mountProp = opt case "z", "Z": zZ = opt diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index bf74ca954..83d5c20cb 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -344,7 +344,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { Type: "bind", Source: srcPath, Destination: dstPath, - Options: []string{"bind", "private"}, + Options: []string{"bind", "rprivate"}, } if c.IsReadOnly() && dstPath != "/dev/shm" { newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev") diff --git a/libpod/kube.go b/libpod/kube.go index cd5064c84..067e7827d 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -327,7 +327,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { period := *c.config.Spec.Linux.Resources.CPU.Period if quota > 0 && period > 0 { - cpuLimitMilli := int64(1000 * float64(quota) / float64(period)) + cpuLimitMilli := int64(1000 * util.PeriodAndQuotaToCores(period, quota)) // Kubernetes: precision finer than 1m is not allowed if cpuLimitMilli >= 1 { diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go index e4e6d87e6..055a243c0 100644 --- a/libpod/runtime_volume.go +++ b/libpod/runtime_volume.go @@ -86,8 +86,8 @@ func (r *Runtime) HasVolume(name string) (bool, error) { // Volumes retrieves all volumes // Filters can be provided which will determine which volumes are included in the -// output. Multiple filters are handled by ANDing their output, so only volumes -// matching all filters are returned +// output. If multiple filters are used, a volume will be returned if +// any of the filters are matched func (r *Runtime) Volumes(filters ...VolumeFilter) ([]*Volume, error) { r.lock.RLock() defer r.lock.RUnlock() @@ -101,11 +101,15 @@ func (r *Runtime) Volumes(filters ...VolumeFilter) ([]*Volume, error) { return nil, err } + if len(filters) == 0 { + return vols, nil + } + volsFiltered := make([]*Volume, 0, len(vols)) for _, vol := range vols { - include := true + include := false for _, filter := range filters { - include = include && filter(vol) + include = include || filter(vol) } if include { diff --git a/nix/default.nix b/nix/default.nix index cc8786ce0..a1a8c5287 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -44,7 +44,7 @@ let export CFLAGS='-static' export LDFLAGS='-s -w -static-libgcc -static' export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"' - export BUILDTAGS='static netgo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper seccomp apparmor selinux' + export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper seccomp apparmor selinux' ''; buildPhase = '' patchShebangs . diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 57de0f3b1..fbba00984 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -36,8 +36,6 @@ const ( kubeDirectoryPermission = 0755 // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath kubeFilePermission = 0644 - // Kubernetes sets CPUPeriod to 100000us (100ms): https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ - defaultCPUPeriod = 100000 ) func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { @@ -515,10 +513,9 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container return nil, errors.Wrap(err, "Failed to set CPU quota") } if milliCPU > 0 { - containerConfig.Resources.CPUPeriod = defaultCPUPeriod - // CPU quota is a fraction of the period: milliCPU / 1000.0 * period - // Or, without floating point math: - containerConfig.Resources.CPUQuota = milliCPU * defaultCPUPeriod / 1000 + period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000) + containerConfig.Resources.CPUPeriod = period + containerConfig.Resources.CPUQuota = quota } containerConfig.Resources.Memory, err = quantityToInt64(containerYAML.Resources.Limits.Memory()) diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index ebf5ec196..b441daf08 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -445,7 +445,7 @@ func getBindMount(args []string) (spec.Mount, error) { } setExec = true newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z": + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": newMount.Options = append(newMount.Options, kv[0]) case "bind-propagation": if len(kv) == 1 { diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index fcb7641d2..2d40dba8f 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -350,3 +350,8 @@ func deviceFromPath(path string) (*spec.LinuxDevice, error) { Minor: int64(unix.Minor(devNumber)), }, nil } + +func supportAmbientCapabilities() bool { + err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0) + return err == nil +} diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index d17cd4a9a..dee140282 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -135,7 +135,9 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec.Process.Capabilities.Bounding = caplist configSpec.Process.Capabilities.Inheritable = caplist - if s.User == "" || s.User == "root" || s.User == "0" { + user := strings.Split(s.User, ":")[0] + + if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist } else { @@ -145,6 +147,12 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } configSpec.Process.Capabilities.Effective = userCaps configSpec.Process.Capabilities.Permitted = userCaps + + // Ambient capabilities were added to Linux 4.3. Set ambient + // capabilities only when the kernel supports them. + if supportAmbientCapabilities() { + configSpec.Process.Capabilities.Ambient = userCaps + } } g.SetProcessNoNewPrivileges(s.NoNewPrivileges) diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index eab2657e3..580aaf4f2 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -57,7 +57,7 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string return nil, errors.Wrapf(ErrDupeMntOption, "only one of 'rw' and 'ro' can be used") } foundWrite = true - case "private", "rprivate", "slave", "rslave", "shared", "rshared": + case "private", "rprivate", "slave", "rslave", "shared", "rshared", "unbindable", "runbindable": if foundProp { return nil, errors.Wrapf(ErrDupeMntOption, "only one root propagation mode can be used") } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index a9aad657d..415fd169b 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -653,3 +653,26 @@ func CreateCidFile(cidfile string, id string) error { cidFile.Close() return nil } + +// DefaultCPUPeriod is the default CPU period is 100us, which is the same default +// as Kubernetes. +const DefaultCPUPeriod uint64 = 100000 + +// CoresToPeriodAndQuota converts a fraction of cores to the equivalent +// Completely Fair Scheduler (CFS) parameters period and quota. +// +// Cores is a fraction of the CFS period that a container may use. Period and +// Quota are in microseconds. +func CoresToPeriodAndQuota(cores float64) (uint64, int64) { + return DefaultCPUPeriod, int64(cores * float64(DefaultCPUPeriod)) +} + +// PeriodAndQuotaToCores takes the CFS parameters period and quota and returns +// a fraction that represents the limit to the number of cores that can be +// utilized over the scheduling period. +// +// Cores is a fraction of the CFS period that a container may use. Period and +// Quota are in microseconds. +func PeriodAndQuotaToCores(period uint64, quota int64) float64 { + return float64(quota) / float64(period) +} diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index a9b37844e..cb737bd76 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -257,3 +257,23 @@ func TestValidateSysctlBadSysctl(t *testing.T) { _, err := ValidateSysctls(strSlice) assert.Error(t, err) } + +func TestCoresToPeriodAndQuota(t *testing.T) { + cores := 1.0 + expectedPeriod := DefaultCPUPeriod + expectedQuota := int64(DefaultCPUPeriod) + + actualPeriod, actualQuota := CoresToPeriodAndQuota(cores) + assert.Equal(t, actualPeriod, expectedPeriod, "Period does not match") + assert.Equal(t, actualQuota, expectedQuota, "Quota does not match") +} + +func TestPeriodAndQuotaToCores(t *testing.T) { + var ( + period uint64 = 100000 + quota int64 = 50000 + expectedCores = 0.5 + ) + + assert.Equal(t, PeriodAndQuotaToCores(period, quota), expectedCores) +} diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at index 2c38954b6..aa167a97a 100644 --- a/test/apiv2/30-volumes.at +++ b/test/apiv2/30-volumes.at @@ -35,6 +35,8 @@ t GET libpod/volumes/json 200 \ .[0].CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* # -G --data-urlencode 'filters={"name":["foo1"]}' t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22foo1%22%5D%7D 200 length=1 .[0].Name=foo1 +# -G --data-urlencode 'filters={"name":["foo1","foo2"]}' +t GET libpod/volumes/json?filters=%7B%22name%22%3A%20%5B%22foo1%22%2C%20%22foo2%22%5D%7D 200 length=2 .[0].Name=foo1 .[1].Name=foo2 # -G --data-urlencode 'filters={"name":["notexist"]}' t GET libpod/volumes/json?filters=%7B%22name%22%3A%5B%22notexists%22%5D%7D 200 length=0 diff --git a/test/apiv2/rest_api/__init__.py b/test/apiv2/rest_api/__init__.py index e69de29bb..5f0777d58 100644 --- a/test/apiv2/rest_api/__init__.py +++ b/test/apiv2/rest_api/__init__.py @@ -0,0 +1,132 @@ +import configparser +import json +import os +import shutil +import subprocess +import sys +import tempfile + + +class Podman(object): + """ + Instances hold the configuration and setup for running podman commands + """ + + def __init__(self): + """Initialize a Podman instance with global options""" + binary = os.getenv("PODMAN", "bin/podman") + self.cmd = [binary, "--storage-driver=vfs"] + + cgroupfs = os.getenv("CGROUP_MANAGER", "cgroupfs") + self.cmd.append(f"--cgroup-manager={cgroupfs}") + + if os.getenv("DEBUG"): + self.cmd.append("--log-level=debug") + + self.anchor_directory = tempfile.mkdtemp(prefix="podman_restapi_") + self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) + self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) + + os.environ["REGISTRIES_CONFIG_PATH"] = os.path.join( + self.anchor_directory, "registry.conf" + ) + p = configparser.ConfigParser() + p.read_dict( + { + "registries.search": {"registries": "['docker.io']"}, + "registries.insecure": {"registries": "[]"}, + "registries.block": {"registries": "[]"}, + } + ) + with open(os.environ["REGISTRIES_CONFIG_PATH"], "w") as w: + p.write(w) + + os.environ["CNI_CONFIG_PATH"] = os.path.join( + self.anchor_directory, "cni", "net.d" + ) + os.makedirs(os.environ["CNI_CONFIG_PATH"], exist_ok=True) + self.cmd.append("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"]) + cni_cfg = os.path.join( + os.environ["CNI_CONFIG_PATH"], "87-podman-bridge.conflist" + ) + # json decoded and encoded to ensure legal json + buf = json.loads( + """ + { + "cniVersion": "0.3.0", + "name": "podman", + "plugins": [{ + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.88.0.0/16", + "routes": [{ + "dst": "0.0.0.0/0" + }] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] + } + """ + ) + with open(cni_cfg, "w") as w: + json.dump(buf, w) + + def open(self, command, *args, **kwargs): + """Podman initialized instance to run a given command + + :param self: Podman instance + :param command: podman sub-command to run + :param args: arguments and options for command + :param kwargs: See subprocess.Popen() for shell keyword + :return: subprocess.Popen() instance configured to run podman instance + """ + cmd = self.cmd.copy() + cmd.append(command) + cmd.extend(args) + + shell = kwargs.get("shell", False) + + return subprocess.Popen( + cmd, + shell=shell, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + def run(self, command, *args, **kwargs): + """Podman initialized instance to run a given command + + :param self: Podman instance + :param command: podman sub-command to run + :param args: arguments and options for command + :param kwargs: See subprocess.Popen() for shell and check keywords + :return: subprocess.Popen() instance configured to run podman instance + """ + cmd = self.cmd.copy() + cmd.append(command) + cmd.extend(args) + + check = kwargs.get("check", False) + shell = kwargs.get("shell", False) + + return subprocess.run( + cmd, + shell=shell, + check=check, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + def tear_down(self): + shutil.rmtree(self.anchor_directory, ignore_errors=True) diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index 3376f8402..5dfd1fc02 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -1,5 +1,4 @@ import json -import os import subprocess import sys import time @@ -9,27 +8,25 @@ from multiprocessing import Process import requests from dateutil.parser import parse +from test.apiv2.rest_api import Podman + PODMAN_URL = "http://localhost:8080" def _url(path): - return PODMAN_URL + "/v1.0.0/libpod" + path - - -def podman(): - binary = os.getenv("PODMAN_BINARY") - if binary is None: - binary = "bin/podman" - return binary + return PODMAN_URL + "/v2.0.0/libpod" + path def ctnr(path): - r = requests.get(_url("/containers/json?all=true")) try: + r = requests.get(_url("/containers/json?all=true")) ctnrs = json.loads(r.text) except Exception as e: - sys.stderr.write("Bad container response: {}/{}".format(r.text, e)) - raise e + msg = f"Bad container response: {e}" + if r is not None: + msg = msg + " " + r.text + sys.stderr.write(msg + "\n") + raise return path.format(ctnrs[0]["Id"]) @@ -44,50 +41,50 @@ def validateObjectFields(buffer): class TestApi(unittest.TestCase): - podman = None + podman = None # initialized podman configuration for tests + service = None # podman service instance def setUp(self): super().setUp() - if TestApi.podman.poll() is not None: - sys.stderr.write(f"podman service returned {TestApi.podman.returncode}\n") - sys.exit(2) - requests.get( - _url("/images/create?fromSrc=docker.io%2Falpine%3Alatest")) - # calling out to podman is easier than the API for running a container - subprocess.run([podman(), "run", "alpine", "/bin/ls"], - check=True, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + + try: + TestApi.podman.run("run", "alpine", "/bin/ls", check=True) + except subprocess.CalledProcessError as e: + if e.stdout: + sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8")) + if e.stderr: + sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8")) + raise @classmethod def setUpClass(cls): super().setUpClass() - TestApi.podman = subprocess.Popen( - [ - podman(), "system", "service", "tcp:localhost:8080", - "--log-level=debug", "--time=0" - ], - shell=False, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, + TestApi.podman = Podman() + TestApi.service = TestApi.podman.open( + "system", "service", "tcp:localhost:8080", "--log-level=debug", "--time=0" ) + # give the service some time to be ready... time.sleep(2) + returncode = TestApi.service.poll() + if returncode is not None: + raise subprocess.CalledProcessError(returncode, "podman system service") + + r = requests.post(_url("/images/pull?reference=docker.io%2Falpine%3Alatest")) + if r.status_code != 200: + raise subprocess.CalledProcessError( + r.status_code, f"podman images pull docker.io/alpine:latest {r.text}" + ) + @classmethod def tearDownClass(cls): - TestApi.podman.terminate() - stdout, stderr = TestApi.podman.communicate(timeout=0.5) + TestApi.service.terminate() + stdout, stderr = TestApi.service.communicate(timeout=0.5) if stdout: - print("\nService Stdout:\n" + stdout.decode('utf-8')) + sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8")) if stderr: - print("\nService Stderr:\n" + stderr.decode('utf-8')) - - if TestApi.podman.returncode > 0: - sys.stderr.write(f"podman exited with error code {TestApi.podman.returncode}\n") - sys.exit(2) - + sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8")) return super().tearDownClass() def test_info(self): @@ -160,6 +157,7 @@ class TestApi(unittest.TestCase): self.assertIsNone(r.text) def test_attach_containers(self): + self.skipTest("FIXME: Test timeouts") r = requests.post(_url(ctnr("/containers/{}/attach")), timeout=5) self.assertIn(r.status_code, (101, 500), r.text) @@ -242,5 +240,5 @@ class TestApi(unittest.TestCase): self.assertEqual(r.status_code, 200, r.text) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/apiv2/rest_api/test_rest_v1_0_0.py b/test/apiv2/rest_api/v1_test_rest_v1_0_0.py index 2e574e015..acd6273ef 100644 --- a/test/apiv2/rest_api/test_rest_v1_0_0.py +++ b/test/apiv2/rest_api/v1_test_rest_v1_0_0.py @@ -43,16 +43,16 @@ class TestApi(unittest.TestCase): def setUp(self): super().setUp() if TestApi.podman.poll() is not None: - sys.stderr.write("podman service returned {}", - TestApi.podman.returncode) + sys.stderr.write("podman service returned {}", TestApi.podman.returncode) sys.exit(2) - requests.get( - _url("/images/create?fromSrc=docker.io%2Falpine%3Alatest")) + requests.get(_url("/images/create?fromSrc=docker.io%2Falpine%3Alatest")) # calling out to podman is easier than the API for running a container - subprocess.run([podman(), "run", "alpine", "/bin/ls"], - check=True, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [podman(), "run", "alpine", "/bin/ls"], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) @classmethod def setUpClass(cls): @@ -60,8 +60,12 @@ class TestApi(unittest.TestCase): TestApi.podman = subprocess.Popen( [ - podman(), "system", "service", "tcp:localhost:8080", - "--log-level=debug", "--time=0" + podman(), + "system", + "service", + "tcp:localhost:8080", + "--log-level=debug", + "--time=0", ], shell=False, stdin=subprocess.DEVNULL, @@ -75,13 +79,14 @@ class TestApi(unittest.TestCase): TestApi.podman.terminate() stdout, stderr = TestApi.podman.communicate(timeout=0.5) if stdout: - print("\nService Stdout:\n" + stdout.decode('utf-8')) + print("\nService Stdout:\n" + stdout.decode("utf-8")) if stderr: - print("\nService Stderr:\n" + stderr.decode('utf-8')) + print("\nService Stderr:\n" + stderr.decode("utf-8")) if TestApi.podman.returncode > 0: - sys.stderr.write("podman exited with error code {}\n".format( - TestApi.podman.returncode)) + sys.stderr.write( + "podman exited with error code {}\n".format(TestApi.podman.returncode) + ) sys.exit(2) return super().tearDownClass() @@ -222,13 +227,14 @@ class TestApi(unittest.TestCase): def validateObjectFields(self, buffer): - objs = json.loads(buffer) - if not isinstance(objs, dict): - for o in objs: - _ = o["Id"] - else: - _ = objs["Id"] - return objs - -if __name__ == '__main__': + objs = json.loads(buffer) + if not isinstance(objs, dict): + for o in objs: + _ = o["Id"] + else: + _ = objs["Id"] + return objs + + +if __name__ == "__main__": unittest.main() diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 3814d161d..678b2c882 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -613,12 +613,10 @@ func SkipIfRootlessCgroupsV1(reason string) { } } -func SkipIfUnprevilegedCPULimits() { +func SkipIfUnprivilegedCPULimits() { info := GetHostDistributionInfo() - if isRootless() && - info.Distribution == "fedora" && - (info.Version == "31" || info.Version == "32") { - ginkgo.Skip("Rootless Fedora doesn't have permission to set CPU limits before version 33") + if isRootless() && info.Distribution == "fedora" { + ginkgo.Skip("Rootless Fedora doesn't have permission to set CPU limits") } } diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 6dcfa9bd8..1d683e987 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -1406,7 +1406,7 @@ spec: It("podman play kube allows setting resource limits", func() { SkipIfContainerized("Resource limits require a running systemd") SkipIfRootlessCgroupsV1("Limits require root or cgroups v2") - SkipIfUnprevilegedCPULimits() + SkipIfUnprivilegedCPULimits() podmanTest.CgroupManager = "systemd" var ( diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index e6bba9f67..deb4419af 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -315,6 +315,39 @@ var _ = Describe("Podman run", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) + + session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) + + session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + + session = podmanTest.Podman([]string{"run", "--user=0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + + session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) + + if os.Geteuid() > 0 { + if os.Getenv("SKIP_USERNS") != "" { + Skip("Skip userns tests.") + } + if _, err := os.Stat("/proc/self/uid_map"); err != nil { + Skip("User namespaces not supported.") + } + session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) + } }) It("podman run user capabilities test with image", func() { diff --git a/test/e2e/toolbox_test.go b/test/e2e/toolbox_test.go index fbff8d19e..822159fc2 100644 --- a/test/e2e/toolbox_test.go +++ b/test/e2e/toolbox_test.go @@ -30,10 +30,12 @@ import ( "os" "os/exec" "os/user" + "path" "strconv" "strings" "syscall" + "github.com/containers/podman/v2/pkg/rootless" . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -371,10 +373,23 @@ var _ = Describe("Toolbox-specific testing", func() { currentUser, err := user.Current() Expect(err).To(BeNil()) + session = podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:%s", currentUser.HomeDir, currentUser.HomeDir), "--userns=keep-id", fedoraToolbox, "sh", "-c", "echo $HOME"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir)) + + if rootless.IsRootless() { + location := path.Dir(currentUser.HomeDir) + volumeArg := fmt.Sprintf("%s:%s", location, location) + session = podmanTest.Podman([]string{"run", + "--userns=keep-id", + "--volume", volumeArg, + fedoraToolbox, "sh", "-c", "echo $HOME"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir)) + } }) }) diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go index 1cb6440aa..cda118bf1 100644 --- a/test/e2e/volume_ls_test.go +++ b/test/e2e/volume_ls_test.go @@ -110,4 +110,27 @@ var _ = Describe("Podman volume ls", func() { Expect(lsDangling.ExitCode()).To(Equal(0)) Expect(lsDangling.OutputToString()).To(ContainSubstring(volName1)) }) + It("podman ls volume with multiple --filter flag", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "foo=bar", "myvol"}) + volName := session.OutputToString() + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create", "--label", "foo2=bar2", "anothervol"}) + anotherVol := session.OutputToString() + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo", "--filter", "label=foo2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(3)) + Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName)) + Expect(session.OutputToStringArray()[2]).To(ContainSubstring(anotherVol)) + + }) }) diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 2d27ccba1..82bfa5ce3 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.23.7 +1.23.8 diff --git a/vendor/github.com/containers/storage/drivers/chown_unix.go b/vendor/github.com/containers/storage/drivers/chown_unix.go index 1cadb089f..7c2a73f6b 100644 --- a/vendor/github.com/containers/storage/drivers/chown_unix.go +++ b/vendor/github.com/containers/storage/drivers/chown_unix.go @@ -50,22 +50,22 @@ func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools. if uid != int(st.Uid) || gid != int(st.Gid) { cap, err := system.Lgetxattr(path, "security.capability") if err != nil && err != system.ErrNotSupportedPlatform { - return fmt.Errorf("%s: Lgetxattr(%q): %v", os.Args[0], path, err) + return fmt.Errorf("%s: %v", os.Args[0], err) } // Make the change. - if err := os.Lchown(path, uid, gid); err != nil { - return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err) + if err := system.Lchown(path, uid, gid); err != nil { + return fmt.Errorf("%s: %v", os.Args[0], err) } // Restore the SUID and SGID bits if they were originally set. if (info.Mode()&os.ModeSymlink == 0) && info.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 { - if err := os.Chmod(path, info.Mode()); err != nil { - return fmt.Errorf("%s: chmod(%q): %v", os.Args[0], path, err) + if err := system.Chmod(path, info.Mode()); err != nil { + return fmt.Errorf("%s: %v", os.Args[0], err) } } if cap != nil { if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil { - return fmt.Errorf("%s: Lsetxattr(%q): %v", os.Args[0], path, err) + return fmt.Errorf("%s: %v", os.Args[0], err) } } diff --git a/vendor/github.com/containers/storage/drivers/copy/copy_linux.go b/vendor/github.com/containers/storage/drivers/copy/copy_linux.go index 1e380a5ac..5147b01d6 100644 --- a/vendor/github.com/containers/storage/drivers/copy/copy_linux.go +++ b/vendor/github.com/containers/storage/drivers/copy/copy_linux.go @@ -12,6 +12,7 @@ package copy import "C" import ( "container/list" + "errors" "fmt" "io" "os" @@ -98,7 +99,7 @@ func legacyCopy(srcFile io.Reader, dstFile io.Writer) error { func copyXattr(srcPath, dstPath, attr string) error { data, err := system.Lgetxattr(srcPath, attr) - if err != nil && err != unix.EOPNOTSUPP { + if err != nil && !errors.Is(err, unix.EOPNOTSUPP) { return err } if data != nil { @@ -269,7 +270,7 @@ func doCopyXattrs(srcPath, dstPath string) error { } xattrs, err := system.Llistxattr(srcPath) - if err != nil && err != unix.EOPNOTSUPP { + if err != nil && !errors.Is(err, unix.EOPNOTSUPP) { return err } diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 398fe6531..a7cfeadc7 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -42,6 +42,8 @@ var ( untar = chrootarchive.UntarUncompressed ) +const defaultPerms = os.FileMode(0555) + // This backend uses the overlay union filesystem for containers // with diff directories for each layer. @@ -571,15 +573,17 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr if err := idtools.MkdirAllAs(path.Dir(dir), 0700, rootUID, rootGID); err != nil { return err } + perms := defaultPerms if parent != "" { st, err := system.Stat(d.dir(parent)) if err != nil { return err } + perms = os.FileMode(st.Mode()) rootUID = int(st.UID()) rootGID = int(st.GID()) } - if err := idtools.MkdirAs(dir, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(dir, perms, rootUID, rootGID); err != nil { return err } @@ -604,7 +608,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr } } - if err := idtools.MkdirAs(path.Join(dir, "diff"), 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(path.Join(dir, "diff"), perms, rootUID, rootGID); err != nil { return err } @@ -847,7 +851,11 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return "", err } diffN := 1 - _, err = os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN))) + perms := defaultPerms + st, err := os.Stat(filepath.Join(dir, nameWithSuffix("diff", diffN))) + if err == nil { + perms = os.FileMode(st.Mode()) + } for err == nil { absLowers = append(absLowers, filepath.Join(dir, nameWithSuffix("diff", diffN))) relLowers = append(relLowers, dumbJoin(string(link), "..", nameWithSuffix("diff", diffN))) @@ -908,7 +916,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO return "", err } diffDir := path.Join(dir, "diff") - if err := idtools.MkdirAllAs(diffDir, 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAs(diffDir, perms, rootUID, rootGID); err != nil { return "", err } @@ -1241,11 +1249,16 @@ func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMapp // Rotate the diff directories. i := 0 - _, err = os.Stat(nameWithSuffix(diffDir, i)) + perms := defaultPerms + st, err := os.Stat(nameWithSuffix(diffDir, i)) + if err == nil { + perms = os.FileMode(st.Mode()) + } for err == nil { i++ _, err = os.Stat(nameWithSuffix(diffDir, i)) } + for i > 0 { err = os.Rename(nameWithSuffix(diffDir, i-1), nameWithSuffix(diffDir, i)) if err != nil { @@ -1258,13 +1271,13 @@ func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMapp // to the old upper layer in the index. workDir := filepath.Join(dir, "work") if err := os.RemoveAll(workDir); err == nil { - if err := idtools.MkdirAs(workDir, 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(workDir, defaultPerms, rootUID, rootGID); err != nil { return err } } // Re-create the directory that we're going to use as the upper layer. - if err := idtools.MkdirAs(diffDir, 0755, rootUID, rootGID); err != nil { + if err := idtools.MkdirAs(diffDir, perms, rootUID, rootGID); err != nil { return err } return nil diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index f38266a16..9d5a2b425 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -8,7 +8,7 @@ require ( github.com/Microsoft/hcsshim v0.8.9 github.com/docker/go-units v0.4.0 github.com/hashicorp/go-multierror v1.1.0 - github.com/klauspost/compress v1.11.1 + github.com/klauspost/compress v1.11.2 github.com/klauspost/pgzip v1.2.5 github.com/mattn/go-shellwords v1.0.10 github.com/mistifyio/go-zfs v2.1.1+incompatible diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum index 2aad798d8..681f77cbc 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -62,8 +62,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.1 h1:bPb7nMRdOZYDrpPMTA3EInUQrdgoBinqUuSwlGdKDdE= -github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index a70806f40..72f00b8d6 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -1329,6 +1329,7 @@ func (r *layerStore) ApplyDiff(to string, diff io.Reader) (size int64, err error if err != nil { return -1, err } + defer uncompressed.Close() uncompressedDigest := digest.Canonical.Digester() uncompressedCounter := ioutils.NewWriteCounter(uncompressedDigest.Hash()) uidLog := make(map[uint32]struct{}) diff --git a/vendor/github.com/containers/storage/pkg/archive/archive.go b/vendor/github.com/containers/storage/pkg/archive/archive.go index 4472511a2..345da2903 100644 --- a/vendor/github.com/containers/storage/pkg/archive/archive.go +++ b/vendor/github.com/containers/storage/pkg/archive/archive.go @@ -139,6 +139,7 @@ func IsArchivePath(path string) bool { if err != nil { return false } + defer rdr.Close() r := tar.NewReader(rdr) _, err = r.Next() return err == nil @@ -398,7 +399,7 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error { } for _, xattr := range []string{"security.capability", "security.ima"} { capability, err := system.Lgetxattr(path, xattr) - if err != nil && err != system.EOPNOTSUPP && err != system.ErrNotSupportedPlatform { + if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform { return errors.Wrapf(err, "failed to read %q attribute from %q", xattr, path) } if capability != nil { @@ -411,17 +412,17 @@ func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error { // ReadUserXattrToTarHeader reads user.* xattr from filesystem to a tar header func ReadUserXattrToTarHeader(path string, hdr *tar.Header) error { xattrs, err := system.Llistxattr(path) - if err != nil && err != system.EOPNOTSUPP && err != system.ErrNotSupportedPlatform { + if err != nil && !errors.Is(err, system.EOPNOTSUPP) && err != system.ErrNotSupportedPlatform { return err } for _, key := range xattrs { if strings.HasPrefix(key, "user.") { value, err := system.Lgetxattr(path, key) - if err == system.E2BIG { - logrus.Errorf("archive: Skipping xattr for file %s since value is too big: %s", path, key) - continue - } if err != nil { + if errors.Is(err, system.E2BIG) { + logrus.Errorf("archive: Skipping xattr for file %s since value is too big: %s", path, key) + continue + } return err } if hdr.Xattrs == nil { @@ -724,16 +725,16 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L } } - var errors []string + var errs []string for key, value := range hdr.Xattrs { if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { - if err == syscall.ENOTSUP || (err == syscall.EPERM && inUserns) { + if errors.Is(err, syscall.ENOTSUP) || (inUserns && errors.Is(err, syscall.EPERM)) { // We ignore errors here because not all graphdrivers support // xattrs *cough* old versions of AUFS *cough*. However only // ENOTSUP should be emitted in that case, otherwise we still // bail. We also ignore EPERM errors if we are running in a // user namespace. - errors = append(errors, err.Error()) + errs = append(errs, err.Error()) continue } return err @@ -741,9 +742,9 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L } - if len(errors) > 0 { + if len(errs) > 0 { logrus.WithFields(logrus.Fields{ - "errors": errors, + "errors": errs, }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them") } diff --git a/vendor/github.com/containers/storage/pkg/archive/changes_linux.go b/vendor/github.com/containers/storage/pkg/archive/changes_linux.go index ecfa45d73..da86e0c05 100644 --- a/vendor/github.com/containers/storage/pkg/archive/changes_linux.go +++ b/vendor/github.com/containers/storage/pkg/archive/changes_linux.go @@ -2,6 +2,7 @@ package archive import ( "bytes" + "errors" "fmt" "os" "path/filepath" @@ -86,21 +87,21 @@ func walkchunk(path string, fi os.FileInfo, dir string, root *FileInfo) error { } info.stat = stat info.capability, err = system.Lgetxattr(cpath, "security.capability") // lgetxattr(2): fs access - if err != nil && err != system.EOPNOTSUPP { + if err != nil && !errors.Is(err, system.EOPNOTSUPP) { return err } xattrs, err := system.Llistxattr(cpath) - if err != nil && err != system.EOPNOTSUPP { + if err != nil && !errors.Is(err, system.EOPNOTSUPP) { return err } for _, key := range xattrs { if strings.HasPrefix(key, "user.") { value, err := system.Lgetxattr(cpath, key) - if err == system.E2BIG { - logrus.Errorf("archive: Skipping xattr for file %s since value is too big: %s", cpath, key) - continue - } if err != nil { + if errors.Is(err, system.E2BIG) { + logrus.Errorf("archive: Skipping xattr for file %s since value is too big: %s", cpath, key) + continue + } return err } if info.xattrs == nil { diff --git a/vendor/github.com/containers/storage/pkg/system/chmod.go b/vendor/github.com/containers/storage/pkg/system/chmod.go new file mode 100644 index 000000000..a01d8abfb --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/chmod.go @@ -0,0 +1,17 @@ +package system + +import ( + "errors" + "os" + "syscall" +) + +func Chmod(name string, mode os.FileMode) error { + err := os.Chmod(name, mode) + + for err != nil && errors.Is(err, syscall.EINTR) { + err = os.Chmod(name, mode) + } + + return err +} diff --git a/vendor/github.com/containers/storage/pkg/system/lchown.go b/vendor/github.com/containers/storage/pkg/system/lchown.go new file mode 100644 index 000000000..eb2d8b464 --- /dev/null +++ b/vendor/github.com/containers/storage/pkg/system/lchown.go @@ -0,0 +1,20 @@ +package system + +import ( + "os" + "syscall" +) + +func Lchown(name string, uid, gid int) error { + err := syscall.Lchown(name, uid, gid) + + for err == syscall.EINTR { + err = syscall.Lchown(name, uid, gid) + } + + if err != nil { + return &os.PathError{Op: "lchown", Path: name, Err: err} + } + + return nil +} diff --git a/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go index e94bb5d5c..10355848b 100644 --- a/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go +++ b/vendor/github.com/containers/storage/pkg/system/xattrs_linux.go @@ -2,6 +2,7 @@ package system import ( "bytes" + "os" "golang.org/x/sys/unix" ) @@ -26,7 +27,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) { // Buffer too small, use zero-sized buffer to get the actual size sz, errno = unix.Lgetxattr(path, attr, []byte{}) if errno != nil { - return nil, errno + return nil, &os.PathError{Op: "lgetxattr", Path: path, Err: errno} } dest = make([]byte, sz) sz, errno = unix.Lgetxattr(path, attr, dest) @@ -36,7 +37,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) { case errno == unix.ENODATA: return nil, nil case errno != nil: - return nil, errno + return nil, &os.PathError{Op: "lgetxattr", Path: path, Err: errno} } return dest[:sz], nil @@ -45,7 +46,11 @@ func Lgetxattr(path string, attr string) ([]byte, error) { // Lsetxattr sets the value of the extended attribute identified by attr // and associated with the given path in the file system. func Lsetxattr(path string, attr string, data []byte, flags int) error { - return unix.Lsetxattr(path, attr, data, flags) + if err := unix.Lsetxattr(path, attr, data, flags); err != nil { + return &os.PathError{Op: "lsetxattr", Path: path, Err: err} + } + + return nil } // Llistxattr lists extended attributes associated with the given path @@ -58,14 +63,14 @@ func Llistxattr(path string) ([]string, error) { // Buffer too small, use zero-sized buffer to get the actual size sz, errno = unix.Llistxattr(path, []byte{}) if errno != nil { - return nil, errno + return nil, &os.PathError{Op: "llistxattr", Path: path, Err: errno} } dest = make([]byte, sz) sz, errno = unix.Llistxattr(path, dest) } if errno != nil { - return nil, errno + return nil, &os.PathError{Op: "llistxattr", Path: path, Err: errno} } var attrs []string diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index ea3e51082..07f7285f0 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -251,14 +251,14 @@ For streaming use a simple setup could look like this: import "github.com/klauspost/compress/zstd" func Decompress(in io.Reader, out io.Writer) error { - d, err := zstd.NewReader(input) + d, err := zstd.NewReader(in) if err != nil { return err } defer d.Close() // Copy content... - _, err := io.Copy(out, d) + _, err = io.Copy(out, d) return err } ``` diff --git a/vendor/modules.txt b/vendor/modules.txt index d0b50aceb..a7b35a318 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -166,7 +166,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/host github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process -# github.com/containers/storage v1.23.7 +# github.com/containers/storage v1.23.8 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -334,7 +334,7 @@ github.com/inconshreveable/mousetrap github.com/ishidawataru/sctp # github.com/json-iterator/go v1.1.10 github.com/json-iterator/go -# github.com/klauspost/compress v1.11.1 +# github.com/klauspost/compress v1.11.2 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 |