diff options
-rw-r--r-- | cmd/podman/common/completion.go | 6 | ||||
-rw-r--r-- | cmd/podman/images/push.go | 4 | ||||
-rw-r--r-- | docs/source/markdown/podman-push.1.md | 4 | ||||
-rw-r--r-- | libpod/container.go | 5 | ||||
-rw-r--r-- | libpod/container_top_linux.go | 18 | ||||
-rw-r--r-- | pkg/domain/entities/images.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/images.go | 9 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 8 | ||||
-rw-r--r-- | pkg/specgenutil/specgen.go | 17 | ||||
-rw-r--r-- | test/e2e/push_test.go | 32 | ||||
-rw-r--r-- | test/e2e/top_test.go | 5 | ||||
-rw-r--r-- | test/system/180-blkio.bats | 69 |
12 files changed, 170 insertions, 9 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 4cb29383a..cb3efe592 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1289,3 +1289,9 @@ func AutocompleteCheckpointCompressType(cmd *cobra.Command, args []string, toCom types := []string{"gzip", "none", "zstd"} return types, cobra.ShellCompDirectiveNoFileComp } + +// AutocompleteCompressionFormat - Autocomplete compression-format type options. +func AutocompleteCompressionFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"gzip", "zstd", "zstd:chunked"} + return types, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go index cf787a71f..37ace3ffe 100644 --- a/cmd/podman/images/push.go +++ b/cmd/podman/images/push.go @@ -108,6 +108,10 @@ func pushFlags(cmd *cobra.Command) { flags.BoolVar(&pushOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") + compressionFormat := "compression-format" + flags.StringVar(&pushOptions.CompressionFormat, compressionFormat, "", "compression format to use") + _ = cmd.RegisterFlagCompletionFunc(compressionFormat, common.AutocompleteCompressionFormat) + if registry.IsRemote() { _ = flags.MarkHidden("cert-dir") _ = flags.MarkHidden("compress") diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md index 55f294158..19c64a7e3 100644 --- a/docs/source/markdown/podman-push.1.md +++ b/docs/source/markdown/podman-push.1.md @@ -71,6 +71,10 @@ Please refer to containers-certs.d(5) for details. (This option is not available Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as source) Note: This flag can only be set when using the **dir** transport +#### **--compression-format** *COMPRESSION* + +Specifies the compression format to use. Supported values are: `gzip`, `zstd` and `zstd:chunked`. The default is `gzip`. + #### **--digestfile** *Digestfile* After copying the image, write the digest of the resulting image to the file. (This option is not available with the remote Podman client) diff --git a/libpod/container.go b/libpod/container.go index 482af43f3..2b74a1943 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -939,6 +939,11 @@ func (c *Container) cGroupPath() (string, error) { procPath := fmt.Sprintf("/proc/%d/cgroup", c.state.PID) lines, err := ioutil.ReadFile(procPath) if err != nil { + // If the file doesn't exist, it means the container could have been terminated + // so report it. + if os.IsNotExist(err) { + return "", errors.Wrapf(define.ErrCtrStopped, "cannot get cgroup path unless container %s is running", c.ID()) + } return "", err } diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go index 0d4cba85e..d4f4ddfc1 100644 --- a/libpod/container_top_linux.go +++ b/libpod/container_top_linux.go @@ -4,6 +4,7 @@ package libpod import ( "bufio" + "fmt" "os" "strconv" "strings" @@ -11,6 +12,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/psgo" + "github.com/google/shlex" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -51,7 +53,21 @@ func (c *Container) Top(descriptors []string) ([]string, error) { return nil, psgoErr } - output, err = c.execPS(descriptors) + // Note that the descriptors to ps(1) must be shlexed (see #12452). + psDescriptors := []string{} + for _, d := range descriptors { + shSplit, err := shlex.Split(d) + if err != nil { + return nil, fmt.Errorf("parsing ps args: %v", err) + } + for _, s := range shSplit { + if s != "" { + psDescriptors = append(psDescriptors, s) + } + } + } + + output, err = c.execPS(psDescriptors) if err != nil { return nil, errors.Wrapf(err, "error executing ps(1) in the container") } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 54f7b5d45..8b0fd2b85 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -208,6 +208,8 @@ type ImagePushOptions struct { SkipTLSVerify types.OptionalBool // Progress to get progress notifications Progress chan types.ProgressProperties + // CompressionFormat is the format to use for the compression of the blobs + CompressionFormat string } // ImageSearchOptions are the arguments for searching images. diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 8b44b869a..7a3451a7d 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -18,6 +18,7 @@ import ( "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/signature" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" @@ -305,6 +306,14 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri pushOptions.SignBy = options.SignBy pushOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + if options.CompressionFormat != "" { + algo, err := compression.AlgorithmByName(options.CompressionFormat) + if err != nil { + return err + } + pushOptions.CompressionFormat = &algo + } + if !options.Quiet { pushOptions.Writer = os.Stderr } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 1b022b912..df5788099 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -329,6 +329,14 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) } + for k, v := range s.WeightDevice { + statT := unix.Stat_t{} + if err := unix.Stat(k, &statT); err != nil { + return nil, errors.Wrapf(err, "failed to inspect '%s' in --blkio-weight-device", k) + } + g.AddLinuxResourcesBlockIOWeightDevice((int64(unix.Major(uint64(statT.Rdev)))), (int64(unix.Minor(uint64(statT.Rdev)))), *v.Weight) + } + BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) g.ClearProcessEnv() diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 7a572e730..637a6a8dd 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -85,7 +85,7 @@ func getIOLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) ( } if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { + if s.WeightDevice, err = parseWeightDevices(c.BlkIOWeightDevice); err != nil { return nil, err } hasLimits = true @@ -791,29 +791,30 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start return &hc, nil } -func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { +func parseWeightDevices(weightDevs []string) (map[string]specs.LinuxWeightDevice, error) { + wd := make(map[string]specs.LinuxWeightDevice) for _, val := range weightDevs { split := strings.SplitN(val, ":", 2) if len(split) != 2 { - return fmt.Errorf("bad format: %s", val) + return nil, fmt.Errorf("bad format: %s", val) } if !strings.HasPrefix(split[0], "/dev/") { - return fmt.Errorf("bad format for device path: %s", val) + return nil, fmt.Errorf("bad format for device path: %s", val) } weight, err := strconv.ParseUint(split[1], 10, 0) if err != nil { - return fmt.Errorf("invalid weight for device: %s", val) + return nil, fmt.Errorf("invalid weight for device: %s", val) } if weight > 0 && (weight < 10 || weight > 1000) { - return fmt.Errorf("invalid weight for device: %s", val) + return nil, fmt.Errorf("invalid weight for device: %s", val) } w := uint16(weight) - s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ + wd[split[0]] = specs.LinuxWeightDevice{ Weight: &w, LeafWeight: nil, } } - return nil + return wd, nil } func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index 7038a09e8..a3b5e31bb 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -2,12 +2,14 @@ package integration import ( "fmt" + "io/ioutil" "os" "path/filepath" "strings" "github.com/containers/podman/v3/pkg/rootless" . "github.com/containers/podman/v3/test/utils" + "github.com/containers/storage/pkg/archive" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -63,6 +65,36 @@ var _ = Describe("Podman push", func() { Expect(session).Should(Exit(0)) }) + It("podman push to oci with compression-format", func() { + SkipIfRemote("Remote push does not support dir transport") + bbdir := filepath.Join(podmanTest.TempDir, "busybox-oci") + session := podmanTest.Podman([]string{"push", "--compression-format=zstd", "--remove-signatures", ALPINE, + fmt.Sprintf("oci:%s", bbdir)}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + foundZstdFile := false + + blobsDir := filepath.Join(bbdir, "blobs/sha256") + + blobs, err := ioutil.ReadDir(blobsDir) + Expect(err).To(BeNil()) + + for _, f := range blobs { + blobPath := filepath.Join(blobsDir, f.Name()) + + sourceFile, err := ioutil.ReadFile(blobPath) + Expect(err).To(BeNil()) + + compressionType := archive.DetectCompression(sourceFile) + if compressionType == archive.Zstd { + foundZstdFile = true + break + } + } + Expect(foundZstdFile).To(BeTrue()) + }) + It("podman push to local registry", func() { SkipIfRemote("Remote does not support --digestfile or --remove-signatures") if podmanTest.Host.Arch == "ppc64le" { diff --git a/test/e2e/top_test.go b/test/e2e/top_test.go index 93c4f3f12..1a71ebf33 100644 --- a/test/e2e/top_test.go +++ b/test/e2e/top_test.go @@ -101,6 +101,11 @@ var _ = Describe("Podman top", func() { result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) + + result = podmanTest.Podman([]string{"top", session.OutputToString(), "ax -o args"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToStringArray()).To(Equal([]string{"COMMAND", "top -d 2"})) }) It("podman top with comma-separated options", func() { diff --git a/test/system/180-blkio.bats b/test/system/180-blkio.bats new file mode 100644 index 000000000..68449681a --- /dev/null +++ b/test/system/180-blkio.bats @@ -0,0 +1,69 @@ +#!/usr/bin/env bats -*- bats -*- +# +# podman blkio-related tests +# + +load helpers + +function teardown() { + lofile=${PODMAN_TMPDIR}/disk.img + if [ -f ${lofile} ]; then + run_podman '?' rm -t 0 --all --force + + while read path dev; do + if [[ "$path" == "$lofile" ]]; then + losetup -d $dev + fi + done < <(losetup -l --noheadings --output BACK-FILE,NAME) + + rm ${lofile} + fi + basic_teardown +} + +@test "podman run --blkio-weight-device" { + + skip_if_rootless "cannot create devices in rootless mode" + + # create loopback device + lofile=${PODMAN_TMPDIR}/disk.img + fallocate -l 1k ${lofile} + losetup -f ${lofile} + + run losetup -l --noheadings --output BACK-FILE,NAME,MAJ:MIN + is "$output" ".\+" "Empty output from losetup" + + lodevice=$(awk "\$1 == \"$lofile\" { print \$2 }" <<<"$output") + lomajmin=$(awk "\$1 == \"$lofile\" { print \$3 }" <<<"$output") + + is "$lodevice" ".\+" "Could not determine device for $lofile" + is "$lomajmin" ".\+" "Could not determine major/minor for $lofile" + + # use bfq io scheduler + run grep -w bfq /sys/block/$(basename ${lodevice})/queue/scheduler + if [ $status -ne 0 ]; then + skip "BFQ scheduler is not supported on the system" + fi + echo bfq > /sys/block/$(basename ${lodevice})/queue/scheduler + + # run podman + if is_cgroupsv2; then + if [ ! -f /sys/fs/cgroup/system.slice/io.bfq.weight ]; then + skip "Kernel does not support BFQ IO scheduler" + fi + run_podman run --device ${lodevice}:${lodevice} --blkio-weight-device ${lodevice}:123 --rm $IMAGE \ + /bin/sh -c "cat /sys/fs/cgroup/\$(sed -e 's/0:://' < /proc/self/cgroup)/io.bfq.weight" + is "${lines[1]}" "${lomajmin}\s\+123" + else + if [ ! -f /sys/fs/cgroup/blkio/system.slice/blkio.bfq.weight_device ]; then + skip "Kernel does not support BFQ IO scheduler" + fi + if [ $(podman_runtime) = "crun" ]; then + # As of crun 1.2, crun doesn't support blkio.bfq.weight_device + skip "crun doesn't support blkio.bfq.weight_device" + fi + run_podman run --device ${lodevice}:${lodevice} --blkio-weight-device ${lodevice}:123 --rm $IMAGE \ + /bin/sh -c "cat /sys/fs/cgroup/blkio/blkio.bfq.weight_device" + is "${lines[1]}" "${lomajmin}\s\+123" + fi +} |