diff options
37 files changed, 266 insertions, 171 deletions
@@ -1,30 +1,35 @@ approvers: + - Luap99 + - TomSweeneyRedHat + - ashley-cui - baude - edsantiago + - flouthoc - giuseppe - jwhonce - - Luap99 + - lsm5 - mheon - mtrmac - rhatdan - saschagrunert - - TomSweeneyRedHat - umohnani8 - vrothberg - zhangguanzhang reviewers: - - ashley-cui + - Luap99 + - QiWang19 + - TomSweeneyRedHat - baude + - cdoern - edsantiago + - flouthoc - giuseppe - jwhonce - - Luap99 + - lsm5 - mheon - mtrmac - - QiWang19 - rhatdan - saschagrunert - - TomSweeneyRedHat - umohnani8 - vrothberg - zhangguanzhang 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/containers/mount.go b/cmd/podman/containers/mount.go index 0397b456f..271fc4c6b 100644 --- a/cmd/podman/containers/mount.go +++ b/cmd/podman/containers/mount.go @@ -81,7 +81,7 @@ func init() { validate.AddLatestFlag(containerMountCommand, &mountOpts.Latest) } -func mount(_ *cobra.Command, args []string) error { +func mount(cmd *cobra.Command, args []string) error { if len(args) > 0 && mountOpts.Latest { return errors.Errorf("--latest and containers cannot be used together") } @@ -116,18 +116,14 @@ func mount(_ *cobra.Command, args []string) error { mrs = append(mrs, mountReporter{r}) } - format := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}" - tmpl, err := report.NewTemplate("mounts").Parse(format) - if err != nil { - return err - } + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() - w, err := report.NewWriterDefault(os.Stdout) + rpt, err = rpt.Parse(report.OriginPodman, "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}") if err != nil { return err } - defer w.Flush() - return tmpl.Execute(w, mrs) + return rpt.Execute(mrs) } func printJSON(reports []*entities.ContainerMountReport) error { diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 712de327c..cebe61b5a 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -220,30 +220,28 @@ func ps(cmd *cobra.Command, _ []string) error { hdrs, format := createPsOut() + var origin report.Origin noHeading, _ := cmd.Flags().GetBool("noheading") if cmd.Flags().Changed("format") { noHeading = noHeading || !report.HasTable(listOpts.Format) - format = report.NormalizeFormat(listOpts.Format) - format = report.EnforceRange(format) + format = listOpts.Format + origin = report.OriginUser + } else { + origin = report.OriginPodman } ns := strings.NewReplacer(".Namespaces.", ".") format = ns.Replace(format) - tmpl, err := report.NewTemplate("list").Parse(format) - if err != nil { - return err - } - - w, err := report.NewWriterDefault(os.Stdout) + rpt, err := report.New(os.Stdout, cmd.Name()).Parse(origin, format) if err != nil { return err } - defer w.Flush() + defer rpt.Flush() headers := func() error { return nil } if !noHeading { headers = func() error { - return tmpl.Execute(w, hdrs) + return rpt.Execute(hdrs) } } @@ -268,10 +266,10 @@ func ps(cmd *cobra.Command, _ []string) error { if err := headers(); err != nil { return err } - if err := tmpl.Execute(w, responses); err != nil { + if err := rpt.Execute(responses); err != nil { return err } - if err := w.Flush(); err != nil { + if err := rpt.Flush(); err != nil { // we usually do not care about Flush() failures but here do not loop if Flush() has failed return err } @@ -282,7 +280,7 @@ func ps(cmd *cobra.Command, _ []string) error { if err := headers(); err != nil { return err } - if err := tmpl.Execute(w, responses); err != nil { + if err := rpt.Execute(responses); err != nil { return err } } diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index d21feaabc..9fba51597 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -126,14 +126,14 @@ func stats(cmd *cobra.Command, args []string) error { if report.Error != nil { return report.Error } - if err := outputStats(report.Stats); err != nil { + if err := outputStats(cmd, report.Stats); err != nil { return err } } return nil } -func outputStats(reports []define.ContainerStats) error { +func outputStats(cmd *cobra.Command, reports []define.ContainerStats) error { headers := report.Headers(define.ContainerStats{}, map[string]string{ "ID": "ID", "UpTime": "CPU TIME", @@ -158,32 +158,27 @@ func outputStats(reports []define.ContainerStats) error { if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } - format := "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\t{{.UpTime}}\t{{.AVGCPU}}\n" - if len(statsOptions.Format) > 0 { - format = report.NormalizeFormat(statsOptions.Format) - } - format = report.EnforceRange(format) - tmpl, err := report.NewTemplate("stats").Parse(format) - if err != nil { - return err - } + rpt := report.New(os.Stdout, cmd.Name()) + defer rpt.Flush() - w, err := report.NewWriterDefault(os.Stdout) + var err error + if cmd.Flags().Changed("format") { + rpt, err = rpt.Parse(report.OriginUser, statsOptions.Format) + } else { + format := "{{range .}}{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\t{{.UpTime}}\t{{.AVGCPU}}\n{{end -}}" + rpt, err = rpt.Parse(report.OriginPodman, format) + } if err != nil { return err } - defer w.Flush() - if len(statsOptions.Format) < 1 { - if err := tmpl.Execute(w, headers); err != nil { + if rpt.RenderHeaders { + if err := rpt.Execute(headers); err != nil { return err } } - if err := tmpl.Execute(w, stats); err != nil { - return err - } - return nil + return rpt.Execute(stats) } type containerStats struct { diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go index 808c6c494..963a251bb 100644 --- a/cmd/podman/containers/top.go +++ b/cmd/podman/containers/top.go @@ -77,7 +77,7 @@ func init() { validate.AddLatestFlag(containerTopCommand, &topOptions.Latest) } -func top(_ *cobra.Command, args []string) error { +func top(cmd *cobra.Command, args []string) error { if topOptions.ListDescriptors { descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { @@ -103,15 +103,13 @@ func top(_ *cobra.Command, args []string) error { return err } - w, err := report.NewWriterDefault(os.Stdout) - if err != nil { - return err - } + rpt := report.New(os.Stdout, cmd.Name()).Init(os.Stdout, 12, 2, 2, ' ', 0) + defer rpt.Flush() for _, proc := range topResponse.Value { - if _, err := fmt.Fprintln(w, proc); err != nil { + if _, err := fmt.Fprintln(rpt.Writer(), proc); err != nil { return err } } - return w.Flush() + return nil } 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/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 25770fdfc..be6e5ab55 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -346,6 +346,11 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } c = tmpFile.Name() } + cfDir := filepath.Dir(c) + if absDir, err := filepath.EvalSymlinks(cfDir); err == nil { + name := filepath.ToSlash(strings.TrimPrefix(c, cfDir+string(filepath.Separator))) + c = filepath.Join(absDir, name) + } containerfile, err := filepath.Abs(c) if err != nil { logrus.Errorf("Cannot find absolute path of %v: %v", c, err) 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/checkpoint_test.go b/test/e2e/checkpoint_test.go index e068a6582..5a2640f33 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -192,7 +192,7 @@ var _ = Describe("Podman checkpoint", func() { ps.WaitWithDefaultTimeout() Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(session1.OutputToString())) - Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) + Expect(ps.OutputToString()).To(Not(ContainSubstring(session2.OutputToString()))) result = podmanTest.Podman([]string{"container", "restore", "second"}) result.WaitWithDefaultTimeout() @@ -228,8 +228,8 @@ var _ = Describe("Podman checkpoint", func() { ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) ps.WaitWithDefaultTimeout() Expect(ps).Should(Exit(0)) - Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse()) - Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) + Expect(ps.OutputToString()).To(Not(ContainSubstring(session1.OutputToString()))) + Expect(ps.OutputToString()).To(Not(ContainSubstring(session2.OutputToString()))) result = podmanTest.Podman([]string{"container", "restore", "-a"}) result.WaitWithDefaultTimeout() @@ -1297,8 +1297,8 @@ var _ = Describe("Podman checkpoint", func() { }) ps.WaitWithDefaultTimeout() Expect(ps).Should(Exit(0)) - Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse()) - Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) + Expect(ps.OutputToString()).To(Not(ContainSubstring(session1.OutputToString()))) + Expect(ps.OutputToString()).To(Not(ContainSubstring(session2.OutputToString()))) result = podmanTest.Podman([]string{ "container", diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index b751c338f..a09666468 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -204,8 +204,7 @@ var _ = Describe("Podman commit", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) image := inspect.InspectImageJSON() - _, ok := image[0].Config.Volumes["/foo"] - Expect(ok).To(BeFalse()) + Expect(image[0].Config.Volumes).To(Not(HaveKey("/foo"))) }) It("podman commit with volume mounts and --include-volumes", func() { @@ -224,8 +223,7 @@ var _ = Describe("Podman commit", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) image := inspect.InspectImageJSON() - _, ok := image[0].Config.Volumes["/foo"] - Expect(ok).To(BeTrue(), ".Config.Volumes[/foo]") + Expect(image[0].Config.Volumes).To(HaveKey("/foo")) r := podmanTest.Podman([]string{"run", "newimage"}) r.WaitWithDefaultTimeout() @@ -250,7 +248,7 @@ var _ = Describe("Podman commit", func() { for _, v := range image[0].Config.Env { envMap[v] = true } - Expect(envMap["TEST=1=1-01=9.01"]).To(BeTrue(), "envMap[TEST=1=1-01=9.01]") + Expect(envMap).To(HaveKey("TEST=1=1-01=9.01")) }) It("podman commit container and print id to external file", func() { diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 6bc230aae..2a6f177f2 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -245,7 +245,7 @@ var _ = Describe("Podman run", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) + Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("search")))) }) It("podman run use containers.conf search domain", func() { diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 52d439f4a..321361382 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -107,9 +107,7 @@ var _ = Describe("Podman create", func() { check := podmanTest.Podman([]string{"inspect", "annotate_test"}) check.WaitWithDefaultTimeout() data := check.InspectContainerToJSON() - value, ok := data[0].Config.Annotations["HELLO"] - Expect(ok).To(BeTrue(), ".Config.Annotations[HELLO]") - Expect(value).To(Equal("WORLD")) + Expect(data[0].Config.Annotations).To(HaveKeyWithValue("HELLO", "WORLD")) }) It("podman create --entrypoint command", func() { @@ -386,10 +384,8 @@ var _ = Describe("Podman create", func() { data := inspect.InspectContainerToJSON() Expect(len(data)).To(Equal(1), "len(InspectContainerToJSON)") Expect(len(data[0].Config.Labels)).To(Equal(2)) - _, ok1 := data[0].Config.Labels["TESTKEY1"] - Expect(ok1).To(BeTrue(), ".Config.Labels[TESTKEY1]") - _, ok2 := data[0].Config.Labels["TESTKEY2"] - Expect(ok2).To(BeTrue(), ".Config.Labels[TESTKEY2]") + Expect(data[0].Config.Labels).To(HaveKey("TESTKEY1")) + Expect(data[0].Config.Labels).To(HaveKey("TESTKEY2")) }) It("podman create with set label", func() { @@ -404,12 +400,8 @@ var _ = Describe("Podman create", func() { data := inspect.InspectContainerToJSON() Expect(len(data)).To(Equal(1)) Expect(len(data[0].Config.Labels)).To(Equal(2)) - val1, ok1 := data[0].Config.Labels["TESTKEY1"] - Expect(ok1).To(BeTrue(), ".Config.Labels[TESTKEY1]") - Expect(val1).To(Equal("value1")) - val2, ok2 := data[0].Config.Labels["TESTKEY2"] - Expect(ok2).To(BeTrue(), ".Config.Labels[TESTKEY2]") - Expect(val2).To(Equal("bar")) + Expect(data[0].Config.Labels).To(HaveKeyWithValue("TESTKEY1", "value1")) + Expect(data[0].Config.Labels).To(HaveKeyWithValue("TESTKEY2", "bar")) }) It("podman create with --restart=on-failure:5 parses correctly", func() { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 62482598a..735334eec 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -606,9 +606,7 @@ var _ = Describe("Podman generate kube", func() { pod := new(v1.Pod) err = yaml.Unmarshal(b, pod) Expect(err).To(BeNil()) - val, found := pod.Annotations[define.BindMountPrefix+vol1] - Expect(found).To(BeTrue(), "pod.Annotations["+vol1+"]") - Expect(val).To(HaveSuffix("z")) + Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix+vol1, HaveSuffix("z"))) rm := podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "test1"}) rm.WaitWithDefaultTimeout() @@ -1071,9 +1069,7 @@ USER test1` err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) - v, ok := pod.GetAnnotations()["io.containers.autoupdate/top"] - Expect(ok).To(Equal(true)) - Expect(v).To(Equal("local")) + Expect(pod.GetAnnotations()).To(HaveKeyWithValue("io.containers.autoupdate/top", "local")) }) It("podman generate kube on pod with auto update labels in all containers", func() { @@ -1100,13 +1096,8 @@ USER test1` Expect(pod.Spec.Containers[1].WorkingDir).To(Equal("/root")) for _, ctr := range []string{"top1", "top2"} { - v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr] - Expect(ok).To(Equal(true)) - Expect(v).To(Equal("registry")) - - v, ok = pod.GetAnnotations()["io.containers.autoupdate.authfile/"+ctr] - Expect(ok).To(Equal(true)) - Expect(v).To(Equal("/some/authfile.json")) + Expect(pod.GetAnnotations()).To(HaveKeyWithValue("io.containers.autoupdate/"+ctr, "registry")) + Expect(pod.GetAnnotations()).To(HaveKeyWithValue("io.containers.autoupdate.authfile/"+ctr, "/some/authfile.json")) } }) }) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 8e21ca606..9cc04e6b0 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -229,11 +229,10 @@ WORKDIR /test result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - found, _ := result.GrepString("<none>") if noneTag { - Expect(found).To(BeTrue()) + Expect(result.OutputToString()).To(ContainSubstring("<none>")) } else { - Expect(found).To(BeFalse()) + Expect(result.OutputToString()).To(Not(ContainSubstring("<none>"))) } } // No "<none>" tag as tagged alpine instances should be present. diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go index ad5634e0c..ac70ebd8c 100644 --- a/test/e2e/load_test.go +++ b/test/e2e/load_test.go @@ -160,8 +160,7 @@ var _ = Describe("Podman load", func() { Expect(result).Should(Exit(125)) errMsg := fmt.Sprintf("remote client supports archives only but %q is a directory", podmanTest.TempDir) - found, _ := result.ErrorGrepString(errMsg) - Expect(found).Should(BeTrue()) + Expect(result.ErrorToString()).To(ContainSubstring(errMsg)) }) It("podman load bogus file", func() { diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index ff306f0f8..d56cde9db 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -140,7 +140,7 @@ var _ = Describe("Podman network", func() { session := podmanTest.Podman([]string{"network", "ls", "--filter", "label=abc"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains(name)).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring(name))) }) It("podman network ID test", func() { @@ -208,7 +208,7 @@ var _ = Describe("Podman network", func() { results := podmanTest.Podman([]string{"network", "ls", "--quiet"}) results.WaitWithDefaultTimeout() Expect(results).Should(Exit(0)) - Expect(results.LineInOutputContains(name)).To(BeFalse()) + Expect(results.OutputToString()).To(Not(ContainSubstring(name))) }) } diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 223fcc5b2..6b0081171 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -98,8 +98,7 @@ var _ = Describe("Podman prune", func() { session := podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - hasNone, _ := session.GrepString("<none>") - Expect(hasNone).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("<none>"))) numImages := len(session.OutputToStringArray()) // Since there's no dangling image, none should be removed. @@ -125,8 +124,7 @@ var _ = Describe("Podman prune", func() { session = podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - hasNone, _ = session.GrepString("<none>") - Expect(hasNone).To(BeTrue()) // ! we have dangling ones + Expect(session.OutputToString()).To(ContainSubstring("<none>")) numImages = len(session.OutputToStringArray()) // Since there's at least one dangling image, prune should @@ -135,7 +133,7 @@ var _ = Describe("Podman prune", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) numPrunedImages := len(session.OutputToStringArray()) - Expect(numPrunedImages >= 1).To(BeTrue()) + Expect(numPrunedImages).To(BeNumerically(">=", 1), "numPrunedImages") // Now make sure that exactly the number of pruned images has // been removed. @@ -189,11 +187,11 @@ var _ = Describe("Podman prune", func() { after := podmanTest.Podman([]string{"images", "-a"}) after.WaitWithDefaultTimeout() - Expect(none).Should(Exit(0)) - hasNoneAfter, result := none.GrepString("<none>") + Expect(after).Should(Exit(0)) + hasNoneAfter, result := after.GrepString("<none>") Expect(hasNoneAfter).To(BeTrue()) - Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) - Expect(len(result) > 0).To(BeTrue()) + Expect(len(after.OutputToStringArray())).To(BeNumerically(">", 1)) + Expect(len(result)).To(BeNumerically(">", 0)) }) It("podman image prune unused images", func() { 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/restart_test.go b/test/e2e/restart_test.go index 6a61c1292..f85a74f47 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -154,8 +154,8 @@ var _ = Describe("Podman restart", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) timeSince := time.Since(startTime) - Expect(timeSince < 10*time.Second).To(BeTrue()) - Expect(timeSince > 2*time.Second).To(BeTrue()) + Expect(timeSince).To(BeNumerically("<", 10*time.Second)) + Expect(timeSince).To(BeNumerically(">", 2*time.Second)) }) It("Podman restart --all", func() { diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index 4f6d974fd..2c2a9688e 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -276,8 +276,7 @@ RUN find $LOCAL session := podmanTest.Podman([]string{"image", "rm"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) - match, _ := session.ErrorGrepString("image name or ID must be specified") - Expect(match).To(BeTrue()) + Expect(session.ErrorToString()).To(ContainSubstring("image name or ID must be specified")) }) It("podman image rm - concurrent with shared layers", func() { diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go index beb6390e0..8b6d535e5 100644 --- a/test/e2e/run_dns_test.go +++ b/test/e2e/run_dns_test.go @@ -44,7 +44,7 @@ var _ = Describe("Podman run dns", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) + Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("search")))) }) It("podman run add bad dns server", func() { diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go index f500a3c7c..29f76bad1 100644 --- a/test/e2e/run_entrypoint_test.go +++ b/test/e2e/run_entrypoint_test.go @@ -129,6 +129,6 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest", "-r"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputStartsWith("Linux")).To(BeFalse()) + Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("Linux")))) }) }) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index c0840c83b..22ec27346 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -551,8 +551,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - match, _ := session.GrepString(hostname) - Expect(match).Should(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring(hostname))) }) It("podman run --net host hostname test", func() { @@ -866,7 +865,6 @@ EXPOSE 2004-2005/tcp`, ALPINE) inspectOut := podmanTest.InspectContainer(ctrName) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Networks)).To(Equal(1)) - _, ok := inspectOut[0].NetworkSettings.Networks["podman"] - Expect(ok).To(BeTrue()) + Expect(inspectOut[0].NetworkSettings.Networks).To(HaveKey("podman")) }) }) diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go index 05cdc7d80..6d1d26914 100644 --- a/test/e2e/run_passwd_test.go +++ b/test/e2e/run_passwd_test.go @@ -38,20 +38,20 @@ var _ = Describe("Podman run passwd", func() { session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("passwd")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("passwd"))) }) It("podman run user specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "bin", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("passwd")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("passwd"))) }) It("podman run UID specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "2:1", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("passwd")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("passwd"))) }) It("podman run UID not specified in container", func() { @@ -77,14 +77,14 @@ USER 1000`, ALPINE) session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group"))) }) It("podman run group specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:bin", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group"))) }) It("podman run non-numeric group not specified in container", func() { @@ -97,7 +97,7 @@ USER 1000`, ALPINE) session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:11", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group"))) }) It("podman run numeric group not specified in container", func() { diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go index 321bf27ac..f1084a3d2 100644 --- a/test/e2e/run_privileged_test.go +++ b/test/e2e/run_privileged_test.go @@ -63,9 +63,7 @@ var _ = Describe("Podman privileged container tests", func() { session := podmanTest.Podman([]string{"run", "--privileged", BB, "mount"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - ok, lines := session.GrepString("sysfs") - Expect(ok).To(BeTrue()) - Expect(lines[0]).To(ContainSubstring("sysfs (rw,")) + Expect(session.OutputToString()).To(ContainSubstring("sysfs (rw,")) }) It("podman privileged CapEff", func() { diff --git a/test/e2e/run_signal_test.go b/test/e2e/run_signal_test.go index 49f456366..6bb325155 100644 --- a/test/e2e/run_signal_test.go +++ b/test/e2e/run_signal_test.go @@ -116,8 +116,7 @@ var _ = Describe("Podman run with --sig-proxy", func() { } session, pid := podmanTest.PodmanPID([]string{"run", "--name", "test2", "--sig-proxy=false", fedoraMinimal, "bash", "-c", sigCatch2}) - ok := WaitForContainer(podmanTest) - Expect(ok).To(BeTrue()) + Expect(WaitForContainer(podmanTest)).To(BeTrue(), "WaitForContainer()") // Kill with given signal // Should be no output, SIGPOLL is usually ignored @@ -132,8 +131,7 @@ var _ = Describe("Podman run with --sig-proxy", func() { session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) - ok, _ = session.GrepString("Received") - Expect(ok).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("Received"))) }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 6c47e9179..3d4c1240e 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -871,7 +871,7 @@ USER bin`, BB) session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.LineInOutputContains("27(video),777,65533(nogroup)")).To(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("27(video),777,65533(nogroup)"))) }) It("podman run with group-add", func() { @@ -1151,8 +1151,7 @@ USER mail`, BB) session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:z", "--volume", vol2 + ":/myvol2:z", fedoraMinimal, "findmnt", "-o", "TARGET,PROPAGATION"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - match, _ := session.GrepString("shared") - Expect(match).Should(BeFalse()) + Expect(session.OutputToString()).To(Not(ContainSubstring("shared"))) }) It("podman run findmnt shared", func() { diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 10c191bd9..74aa69c96 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -103,10 +103,8 @@ var _ = Describe("Podman run with volumes", func() { session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",consistency=delegated,shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - found, matches := session.GrepString(dest) - Expect(found).Should(BeTrue()) - Expect(matches[0]).To(ContainSubstring("rw")) - Expect(matches[0]).To(ContainSubstring("shared")) + Expect(session.OutputToString()).To(ContainSubstring("rw")) + Expect(session.OutputToString()).To(ContainSubstring("shared")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() @@ -195,20 +193,18 @@ var _ = Describe("Podman run with volumes", func() { session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - found, matches := session.GrepString(dest) - Expect(found).Should(BeTrue()) - Expect(matches[0]).To(Not(ContainSubstring("noexec"))) - Expect(matches[0]).To(Not(ContainSubstring("nodev"))) - Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) + output := session.OutputToString() + Expect(output).To(Not(ContainSubstring("noexec"))) + Expect(output).To(Not(ContainSubstring("nodev"))) + Expect(output).To(Not(ContainSubstring("nosuid"))) session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - found, matches = session.GrepString(dest) - Expect(found).Should(BeTrue()) - Expect(matches[0]).To(Not(ContainSubstring("noexec"))) - Expect(matches[0]).To(Not(ContainSubstring("nodev"))) - Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) + output = session.OutputToString() + Expect(output).To(Not(ContainSubstring("noexec"))) + Expect(output).To(Not(ContainSubstring("nodev"))) + Expect(output).To(Not(ContainSubstring("nosuid"))) }) // Container should start when workdir is overlay volume diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 54bc5252e..c67ef7ed2 100644 --- a/test/e2e/search_test.go +++ b/test/e2e/search_test.go @@ -6,7 +6,6 @@ import ( "fmt" "io/ioutil" "os" - "regexp" "strconv" "text/template" @@ -107,10 +106,8 @@ registries = ['{{.Host}}:{{.Port}}']` search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) output := string(search.Out.Contents()) - match, _ := regexp.MatchString(`(?m)NAME\s+DESCRIPTION$`, output) - Expect(match).To(BeTrue()) - match, _ = regexp.MatchString(`(?m)quay.io/libpod/whalesay\s+Static image used for automated testing.+$`, output) - Expect(match).To(BeTrue()) + Expect(output).To(MatchRegexp(`(?m)NAME\s+DESCRIPTION$`)) + Expect(output).To(MatchRegexp(`(?m)quay.io/libpod/whalesay\s+Static image used for automated testing.+$`)) }) It("podman search image with --compatible", func() { @@ -118,8 +115,7 @@ registries = ['{{.Host}}:{{.Port}}']` search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) output := string(search.Out.Contents()) - match, _ := regexp.MatchString(`(?m)NAME\s+DESCRIPTION\s+STARS\s+OFFICIAL\s+AUTOMATED$`, output) - Expect(match).To(BeTrue()) + Expect(output).To(MatchRegexp(`(?m)NAME\s+DESCRIPTION\s+STARS\s+OFFICIAL\s+AUTOMATED$`)) }) It("podman search format flag", func() { @@ -354,8 +350,7 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) - match, _ := search.ErrorGrepString("error") - Expect(match).Should(BeTrue()) + Expect(search.ErrorToString()).To(ContainSubstring("error")) // cleanup resetRegistriesConfigEnv() @@ -397,8 +392,7 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) - match, _ := search.ErrorGrepString("error") - Expect(match).Should(BeTrue()) + Expect(search.ErrorToString()).To(ContainSubstring("error")) // cleanup resetRegistriesConfigEnv() @@ -451,8 +445,7 @@ registries = ['{{.Host}}:{{.Port}}']` Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) - match, _ := search.ErrorGrepString("error") - Expect(match).Should(BeTrue()) + Expect(search.ErrorToString()).To(ContainSubstring("error")) // cleanup resetRegistriesConfigEnv() @@ -474,7 +467,7 @@ registries = ['{{.Host}}:{{.Port}}']` search = podmanTest.Podman([]string{"search", "registry.redhat.io/*openshift*"}) search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) - Expect(len(search.OutputToStringArray()) > 1).To(BeTrue()) + Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) }) It("podman search repository tags", func() { @@ -494,7 +487,7 @@ registries = ['{{.Host}}:{{.Port}}']` search = podmanTest.Podman([]string{"search", "--list-tags", "docker.io/library/"}) search.WaitWithDefaultTimeout() - Expect(len(search.OutputToStringArray()) == 0).To(BeTrue()) + Expect(search.OutputToStringArray()).To(BeEmpty()) }) It("podman search with limit over 100", func() { diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go index 0119e0f7a..a05c1b593 100644 --- a/test/e2e/volume_rm_test.go +++ b/test/e2e/volume_rm_test.go @@ -127,6 +127,6 @@ var _ = Describe("Podman volume rm", func() { session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(len(session.OutputToStringArray()) >= 2).To(BeTrue()) + Expect(len(session.OutputToStringArray())).To(BeNumerically(">=", 2)) }) }) 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 +} |