diff options
148 files changed, 1332 insertions, 622 deletions
diff --git a/.golangci.yml b/.golangci.yml index cf067a58c..f3338b9ae 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,6 @@ run: skip-dirs: - contrib - dependencies - - test skip-files: - swagger.go modules-download-mode: readonly @@ -512,7 +512,7 @@ validate.completions: .PHONY: run-docker-py-tests run-docker-py-tests: touch test/__init__.py - pytest test/python/docker/ + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf pytest test/python/docker/ -rm test/__init__.py .PHONY: localunit @@ -594,8 +594,8 @@ remotesystem: .PHONY: localapiv2 localapiv2: env PODMAN=./bin/podman ./test/apiv2/test-apiv2 - env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/python - env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/python/docker + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/python + env CONTAINERS_CONF=$(CURDIR)/test/apiv2/containers.conf PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/python/docker .PHONY: remoteapiv2 remoteapiv2: @@ -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/common/create_opts.go b/cmd/podman/common/create_opts.go index aacdfd274..abb55f8c7 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -103,7 +103,9 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c addField(&builder, "type", string(m.Type)) addField(&builder, "source", m.Source) addField(&builder, "target", m.Target) - addField(&builder, "ro", strconv.FormatBool(m.ReadOnly)) + if m.ReadOnly { + addField(&builder, "ro", "true") + } addField(&builder, "consistency", string(m.Consistency)) // Map any specialized mount options that intersect between *Options and cli options switch m.Type { 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/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index 4c27dfa4b..899d14c96 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -82,9 +82,16 @@ function _run_bindings() { # shellcheck disable=SC2155 export PATH=$PATH:$GOSRC/hack + # if logformatter sees this, it can link directly to failing source lines + local gitcommit_magic= + if [[ -n "$GIT_COMMIT" ]]; then + gitcommit_magic="/define.gitCommit=${GIT_COMMIT}" + fi + # Subshell needed so logformatter will write output in cwd; if it runs in # the subdir, .cirrus.yml will not find the html'ized log (cd pkg/bindings/test && \ + echo "$gitcommit_magic" && \ ginkgo -progress -trace -noColor -debug -timeout 30m -r -v) |& logformatter } diff --git a/contrib/podmanimage/stable/Dockerfile b/contrib/podmanimage/stable/Dockerfile index 7950269d2..b0b5bb33b 100644 --- a/contrib/podmanimage/stable/Dockerfile +++ b/contrib/podmanimage/stable/Dockerfile @@ -19,11 +19,11 @@ RUN useradd podman; \ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + # Note VOLUME options must always happen after the chown call above # RUN commands can not modify existing volumes VOLUME /var/lib/containers diff --git a/contrib/podmanimage/testing/Dockerfile b/contrib/podmanimage/testing/Dockerfile index 03da05806..e7228ea42 100644 --- a/contrib/podmanimage/testing/Dockerfile +++ b/contrib/podmanimage/testing/Dockerfile @@ -19,11 +19,11 @@ RUN useradd podman; \ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + # Note VOLUME options must always happen after the chown call above # RUN commands can not modify existing volumes VOLUME /var/lib/containers diff --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile index 89ec6e39b..864227f89 100644 --- a/contrib/podmanimage/upstream/Dockerfile +++ b/contrib/podmanimage/upstream/Dockerfile @@ -68,11 +68,11 @@ RUN useradd podman; \ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + # Note VOLUME options must always happen after the chown call above # RUN commands can not modify existing volumes VOLUME /var/lib/containers diff --git a/contrib/systemd/auto-update/podman-auto-update.service.in b/contrib/systemd/auto-update/podman-auto-update.service.in index de4460d60..a703912e2 100644 --- a/contrib/systemd/auto-update/podman-auto-update.service.in +++ b/contrib/systemd/auto-update/podman-auto-update.service.in @@ -10,4 +10,4 @@ ExecStart=@@PODMAN@@ auto-update ExecStartPost=@@PODMAN@@ image prune -f [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target diff --git a/contrib/systemd/system/podman-restart.service.in b/contrib/systemd/system/podman-restart.service.in index 46193e2c6..1f13e57e1 100644 --- a/contrib/systemd/system/podman-restart.service.in +++ b/contrib/systemd/system/podman-restart.service.in @@ -10,4 +10,4 @@ Environment=LOGGING="--log-level=info" ExecStart=@@PODMAN@@ $LOGGING start --all --filter restart-policy=always [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target diff --git a/contrib/systemd/system/podman.service.in b/contrib/systemd/system/podman.service.in index 132671dff..9a7e04fd4 100644 --- a/contrib/systemd/system/podman.service.in +++ b/contrib/systemd/system/podman.service.in @@ -12,4 +12,4 @@ Environment=LOGGING="--log-level=info" ExecStart=@@PODMAN@@ $LOGGING system service [Install] -WantedBy=multi-user.target +WantedBy=default.target diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 40bd7d8aa..4092696b9 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -114,7 +114,7 @@ given. #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--cgroup-parent**=*path* diff --git a/docs/source/markdown/podman-container-runlabel.1.md b/docs/source/markdown/podman-container-runlabel.1.md index 7078b07e4..2457265dd 100644 --- a/docs/source/markdown/podman-container-runlabel.1.md +++ b/docs/source/markdown/podman-container-runlabel.1.md @@ -41,7 +41,8 @@ Display the label's value of the image having populated its environment variable #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) +Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*[username[:password]]* diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index 7bd31797c..bdcaa8ef1 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -98,7 +98,7 @@ Type=forking PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Generate systemd unit file for a container with `--new` flag @@ -130,7 +130,7 @@ KillMode=none Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Generate systemd unit files for a pod with two simple alpine containers @@ -169,7 +169,7 @@ Type=forking PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Installation of generated systemd unit files. diff --git a/docs/source/markdown/podman-image-sign.1.md b/docs/source/markdown/podman-image-sign.1.md index 5f23bbfaf..8758b8861 100644 --- a/docs/source/markdown/podman-image-sign.1.md +++ b/docs/source/markdown/podman-image-sign.1.md @@ -32,7 +32,7 @@ environment variable. `export REGISTRY_AUTH_FILE=path` #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--directory**, **-d**=*dir* diff --git a/docs/source/markdown/podman-login.1.md b/docs/source/markdown/podman-login.1.md index d7e0152a8..ae1eeeafa 100644 --- a/docs/source/markdown/podman-login.1.md +++ b/docs/source/markdown/podman-login.1.md @@ -37,7 +37,7 @@ environment variable. `export REGISTRY_AUTH_FILE=path` #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--get-login** diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md index 47f3fa25f..8e3f56797 100644 --- a/docs/source/markdown/podman-manifest-add.1.md +++ b/docs/source/markdown/podman-manifest-add.1.md @@ -43,7 +43,7 @@ environment variable. `export REGISTRY_AUTH_FILE=path` #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*creds* diff --git a/docs/source/markdown/podman-manifest-push.1.md b/docs/source/markdown/podman-manifest-push.1.md index b50b8748a..38201555e 100644 --- a/docs/source/markdown/podman-manifest-push.1.md +++ b/docs/source/markdown/podman-manifest-push.1.md @@ -29,7 +29,7 @@ environment variable. `export REGISTRY_AUTH_FILE=path` #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*creds* diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md index 2a511f4bd..075fbbe81 100644 --- a/docs/source/markdown/podman-play-kube.1.md +++ b/docs/source/markdown/podman-play-kube.1.md @@ -119,7 +119,7 @@ Build images even if they are found in the local storage. #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--configmap**=*path* diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md index d91571799..6a1240269 100644 --- a/docs/source/markdown/podman-pull.1.md +++ b/docs/source/markdown/podman-pull.1.md @@ -62,7 +62,7 @@ Default is `${XDG\_RUNTIME\_DIR}/containers/auth.json`, which is set using `podm #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** for details. (This option is not available with the remote Podman client) #### **--creds**=*[username[:password]]* diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md index a145e3194..19c64a7e3 100644 --- a/docs/source/markdown/podman-push.1.md +++ b/docs/source/markdown/podman-push.1.md @@ -63,7 +63,7 @@ value can be entered. The password is entered without echo. #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--compress** @@ -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) @@ -12,9 +12,9 @@ require ( github.com/containernetworking/cni v1.0.1 github.com/containernetworking/plugins v1.0.1 github.com/containers/buildah v1.23.1 - github.com/containers/common v0.46.1-0.20211122213330-d4e7724a0c58 + github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.17.0 + github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c github.com/containers/ocicrypt v1.1.2 github.com/containers/psgo v1.7.1 github.com/containers/storage v1.37.1-0.20211122214631-59ba58582415 @@ -199,7 +199,6 @@ github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw= github.com/containerd/containerd v1.5.5/go.mod h1:oSTh0QpT1w6jYcGmbiSbxv9OSQYaa88mPyWIuU79zyo= github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= @@ -263,14 +262,14 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY= github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ= github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= -github.com/containers/common v0.46.1-0.20211122213330-d4e7724a0c58 h1:d99ZfYePYt1gU5dPvtIdnORNtv/7mkAZUHhCJzR5D5k= -github.com/containers/common v0.46.1-0.20211122213330-d4e7724a0c58/go.mod h1:GrXYaGvQtdKA+fCQLudCTOSGRwZ06MVmRnC7KlI+syY= +github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 h1:h9SrSLSQkvluH/sEJ8X1rlBqCoGJtLvSOu4OGK0Qtuw= +github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91/go.mod h1:PHwsa3UBgbvn2/MwpTQvyHXvVpuwfBrlDBx3GpIRPDQ= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4= -github.com/containers/image/v5 v5.16.1/go.mod h1:mCvIFdzyyP1B0NBcZ80OIuaYqFn/OpFpaOMOMn1kU2M= -github.com/containers/image/v5 v5.17.0 h1:KS5pro80CCsSp5qDBTMmSAWQo+xcBX19zUPExmYX2OQ= github.com/containers/image/v5 v5.17.0/go.mod h1:GnYVusVRFPMMTAAUkrcS8NNSpBp8oyrjOUe04AAmRr4= +github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c h1:WfMOQlq3CDvVe5ONUGfj9/MajskqUHnbo24j24Xg2ZM= +github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c/go.mod h1:boW5ckkT0wu9obDEiOIxrtWQmz1znMuHiVMQPcpHnk0= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= 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/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index cda23a399..54cbe01e9 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -133,8 +133,10 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, return } - w.WriteHeader(http.StatusOK) if err := copyFunc(); err != nil { logrus.Error(err.Error()) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return } + w.WriteHeader(http.StatusOK) } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index d5abb6e44..8837e08ca 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -52,6 +52,13 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { return } + imageName, err := utils.NormalizeToDockerHub(r, body.Config.Image) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + body.Config.Image = imageName + newImage, resolvedName, err := runtime.LibimageRuntime().LookupImage(body.Config.Image, nil) if err != nil { if errors.Cause(err) == storage.ErrImageUnknown { diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 0b7ba8bee..af8b6b63d 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -12,7 +12,6 @@ import ( "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" @@ -56,6 +55,12 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { defer os.Remove(tmpfile.Name()) name := utils.GetName(r) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + imageEngine := abi.ImageEngine{Libpod: runtime} saveOptions := entities.ImageSaveOptions{ @@ -63,7 +68,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { Output: tmpfile.Name(), } - if err := imageEngine.Save(r.Context(), name, nil, saveOptions); err != nil { + if err := imageEngine.Save(r.Context(), possiblyNormalizedName, nil, saveOptions); err != nil { if errors.Cause(err) == storage.ErrImageUnknown { utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name)) return @@ -87,9 +92,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { } func CommitContainer(w http.ResponseWriter, r *http.Request) { - var ( - destImage string - ) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -98,12 +100,12 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { Changes string `schema:"changes"` Comment string `schema:"comment"` Container string `schema:"container"` + Pause bool `schema:"pause"` + Repo string `schema:"repo"` + Tag string `schema:"tag"` // fromSrc string # fromSrc is currently unused - Pause bool `schema:"pause"` - Repo string `schema:"repo"` - Tag string `schema:"tag"` }{ - // This is where you can override the golang default value for one of fields + Tag: "latest", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -116,7 +118,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } sc := runtime.SystemContext() - tag := "latest" options := libpod.ContainerCommitOptions{ Pause: true, } @@ -133,9 +134,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } - if len(query.Tag) > 0 { - tag = query.Tag - } options.Message = query.Comment options.Author = query.Author options.Pause = query.Pause @@ -146,9 +144,15 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } - // I know mitr hates this ... but doing for now + var destImage string if len(query.Repo) > 1 { - destImage = fmt.Sprintf("%s:%s", query.Repo, tag) + destImage = fmt.Sprintf("%s:%s", query.Repo, query.Tag) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, destImage) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + destImage = possiblyNormalizedName } commitImage, err := ctr.Commit(r.Context(), destImage, options) @@ -156,7 +160,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure")) return } - utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint + utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: commitImage.ID()}) // nolint } func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { @@ -195,12 +199,22 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { } } + reference := query.Repo + if query.Repo != "" { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, reference) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + reference = possiblyNormalizedName + } + platformSpecs := strings.Split(query.Platform, "/") opts := entities.ImageImportOptions{ Source: source, Changes: query.Changes, Message: query.Message, - Reference: query.Repo, + Reference: reference, OS: platformSpecs[0], } if len(platformSpecs) > 1 { @@ -250,13 +264,9 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { return } - fromImage := mergeNameAndTagOrDigest(query.FromImage, query.Tag) - - // without this early check this function would return 200 but reported error via body stream soon after - // it's better to let caller know early via HTTP status code that request cannot be processed - _, err := shortnames.Resolve(runtime.SystemContext(), fromImage) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, mergeNameAndTagOrDigest(query.FromImage, query.Tag)) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrap(err, "failed to resolve image name")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) return } @@ -291,7 +301,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { pullResChan := make(chan pullResult) go func() { - pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), fromImage, config.PullPolicyAlways, pullOptions) + pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), possiblyNormalizedName, config.PullPolicyAlways, pullOptions) pullResChan <- pullResult{images: pulledImages, err: err} }() @@ -371,7 +381,13 @@ func GetImage(w http.ResponseWriter, r *http.Request) { // 404 no such // 500 internal name := utils.GetName(r) - newImage, err := utils.GetImage(r, name) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + + newImage, err := utils.GetImage(r, possiblyNormalizedName) if err != nil { // Here we need to fiddle with the error message because docker-py is looking for "No // such image" to determine on how to raise the correct exception. @@ -483,7 +499,16 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { return } - images := query.Names + images := make([]string, len(query.Names)) + for i, img := range query.Names { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, img) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + images[i] = possiblyNormalizedName + } + tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index 7bbc4b99c..f85df02e1 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -24,6 +24,7 @@ import ( "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/channel" "github.com/containers/storage/pkg/archive" + "github.com/docker/docker/pkg/jsonmessage" "github.com/gorilla/schema" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -117,10 +118,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { SecurityOpt string `schema:"securityopt"` ShmSize int `schema:"shmsize"` Squash bool `schema:"squash"` - Tag []string `schema:"t"` + Tags []string `schema:"t"` Target string `schema:"target"` Timestamp int64 `schema:"timestamp"` Ulimits string `schema:"ulimits"` + Secrets string `schema:"secrets"` }{ Dockerfile: "Dockerfile", Registry: "docker.io", @@ -143,6 +145,9 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } + // convert tag formats + tags := query.Tags + // convert addcaps formats var addCaps = []string{} if _, found := r.URL.Query()["addcaps"]; found { @@ -238,9 +243,57 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { dnssearch = m } + var secrets = []string{} + if _, found := r.URL.Query()["secrets"]; found { + var m = []string{} + if err := json.Unmarshal([]byte(query.Secrets), &m); err != nil { + utils.BadRequest(w, "secrets", query.Secrets, err) + return + } + + // for podman-remote all secrets must be picked from context director + // hence modify src so contextdir is added as prefix + + for _, secret := range m { + secretOpt := strings.Split(secret, ",") + if len(secretOpt) > 0 { + modifiedOpt := []string{} + for _, token := range secretOpt { + arr := strings.SplitN(token, "=", 2) + if len(arr) > 1 { + if arr[0] == "src" { + /* move secret away from contextDir */ + /* to make sure we dont accidentally commit temporary secrets to image*/ + builderDirectory, _ := filepath.Split(contextDirectory) + // following path is outside build context + newSecretPath := filepath.Join(builderDirectory, arr[1]) + oldSecretPath := filepath.Join(contextDirectory, arr[1]) + err := os.Rename(oldSecretPath, newSecretPath) + if err != nil { + utils.BadRequest(w, "secrets", query.Secrets, err) + return + } + + modifiedSrc := fmt.Sprintf("src=%s", newSecretPath) + modifiedOpt = append(modifiedOpt, modifiedSrc) + } else { + modifiedOpt = append(modifiedOpt, token) + } + } + } + secrets = append(secrets, strings.Join(modifiedOpt[:], ",")) + } + } + } + var output string - if len(query.Tag) > 0 { - output = query.Tag[0] + if len(tags) > 0 { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, tags[0]) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + output = possiblyNormalizedName } format := buildah.Dockerv2ImageManifest registry := query.Registry @@ -256,9 +309,14 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } } } - var additionalTags []string - if len(query.Tag) > 1 { - additionalTags = query.Tag[1:] + var additionalTags []string // nolint + for i := 1; i < len(tags); i++ { + possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tags[i]) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + additionalTags = append(additionalTags, possiblyNormalizedTag) } var buildArgs = map[string]string{} @@ -403,6 +461,22 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } defer auth.RemoveAuthfile(authfile) + fromImage := query.From + if fromImage != "" { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, fromImage) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + fromImage = possiblyNormalizedName + } + + systemContext := &types.SystemContext{ + AuthFilePath: authfile, + DockerAuthConfig: creds, + } + utils.PossiblyEnforceDockerHub(r, systemContext) + // Channels all mux'ed in select{} below to follow API build protocol stdout := channel.NewWriter(make(chan []byte)) defer stdout.Close() @@ -446,6 +520,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { SeccompProfilePath: seccomp, ShmSize: strconv.Itoa(query.ShmSize), Ulimit: ulimits, + Secrets: secrets, }, CNIConfigDir: rtc.Network.CNIPluginDirs[0], CNIPluginPath: util.DefaultCNIPluginPath, @@ -457,7 +532,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { Err: auxout, Excludes: excludes, ForceRmIntermediateCtrs: query.ForceRm, - From: query.From, + From: fromImage, IgnoreUnrecognizedInstructions: query.Ignore, Isolation: isolation, Jobs: &jobs, @@ -480,10 +555,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { RusageLogFile: query.RusageLogFile, Squash: query.Squash, Target: query.Target, - SystemContext: &types.SystemContext{ - AuthFilePath: authfile, - DockerAuthConfig: creds, - }, + SystemContext: systemContext, } for _, platformSpec := range query.Platform { @@ -546,8 +618,10 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { for { m := struct { - Stream string `json:"stream,omitempty"` - Error string `json:"error,omitempty"` + Stream string `json:"stream,omitempty"` + Error *jsonmessage.JSONError `json:"errorDetail,omitempty"` + // NOTE: `error` is being deprecated check https://github.com/moby/moby/blob/master/pkg/jsonmessage/jsonmessage.go#L148 + ErrorMessage string `json:"error,omitempty"` // deprecate this slowly }{} select { @@ -570,7 +644,10 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } flush() case e := <-stderr.Chan(): - m.Error = string(e) + m.ErrorMessage = string(e) + m.Error = &jsonmessage.JSONError{ + Message: m.ErrorMessage, + } if err := enc.Encode(m); err != nil { logrus.Warnf("Failed to json encode error %v", err) } @@ -584,7 +661,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { logrus.Warnf("Failed to json encode error %v", err) } flush() - for _, tag := range query.Tag { + for _, tag := range tags { m.Stream = fmt.Sprintf("Successfully tagged %s\n", tag) if err := enc.Encode(m); err != nil { logrus.Warnf("Failed to json encode error %v", err) diff --git a/pkg/api/handlers/compat/images_history.go b/pkg/api/handlers/compat/images_history.go index 0c6b9fa88..fb3c2ebd2 100644 --- a/pkg/api/handlers/compat/images_history.go +++ b/pkg/api/handlers/compat/images_history.go @@ -14,9 +14,15 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) - newImage, _, err := runtime.LibimageRuntime().LookupImage(name, nil) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "failed to find image %s", name)) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + + newImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil) + if err != nil { + utils.ImageNotFound(w, possiblyNormalizedName, errors.Wrapf(err, "failed to find image %s", possiblyNormalizedName)) return } history, err := newImage.History(r.Context()) diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 8b6d3d56a..5ecb429ae 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -61,12 +61,24 @@ func PushImage(w http.ResponseWriter, r *http.Request) { if query.Tag != "" { imageName += ":" + query.Tag } + if _, err := utils.ParseStorageReference(imageName); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, errors.Wrapf(err, "image source %q is not a containers-storage-transport reference", imageName)) return } + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, imageName) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + imageName = possiblyNormalizedName + if _, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, nil); err != nil { + utils.ImageNotFound(w, imageName, errors.Wrapf(err, "failed to find image %s", imageName)) + return + } + authconf, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) diff --git a/pkg/api/handlers/compat/images_remove.go b/pkg/api/handlers/compat/images_remove.go index 2dc247c1f..5c06d8de0 100644 --- a/pkg/api/handlers/compat/images_remove.go +++ b/pkg/api/handlers/compat/images_remove.go @@ -34,12 +34,18 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) { } } name := utils.GetName(r) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + imageEngine := abi.ImageEngine{Libpod: runtime} options := entities.ImageRemoveOptions{ Force: query.Force, } - report, rmerrors := imageEngine.Remove(r.Context(), []string{name}, options) + report, rmerrors := imageEngine.Remove(r.Context(), []string{possiblyNormalizedName}, options) if len(rmerrors) > 0 && rmerrors[0] != nil { err := rmerrors[0] if errors.Cause(err) == storage.ErrImageUnknown { diff --git a/pkg/api/handlers/compat/images_tag.go b/pkg/api/handlers/compat/images_tag.go index 5d413a821..3fe13e2f5 100644 --- a/pkg/api/handlers/compat/images_tag.go +++ b/pkg/api/handlers/compat/images_tag.go @@ -14,12 +14,16 @@ import ( func TagImage(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - // /v1.xx/images/(name)/tag name := utils.GetName(r) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } // Allow tagging manifest list instead of resolving instances from manifest lookupOptions := &libimage.LookupImageOptions{ManifestList: true} - newImage, _, err := runtime.LibimageRuntime().LookupImage(name, lookupOptions) + newImage, _, err := runtime.LibimageRuntime().LookupImage(possiblyNormalizedName, lookupOptions) if err != nil { utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name)) return @@ -35,7 +39,14 @@ func TagImage(w http.ResponseWriter, r *http.Request) { } repo := r.Form.Get("repo") tagName := fmt.Sprintf("%s:%s", repo, tag) - if err := newImage.Tag(tagName); err != nil { + + possiblyNormalizedTag, err := utils.NormalizeToDockerHub(r, tagName) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + + if err := newImage.Tag(possiblyNormalizedTag); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index d5eb71aa1..d874165e3 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -3,19 +3,61 @@ package utils import ( "fmt" "net/http" + "strings" "github.com/containers/common/libimage" "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/docker" - "github.com/containers/image/v5/storage" + storageTransport "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" api "github.com/containers/podman/v3/pkg/api/types" + "github.com/containers/podman/v3/pkg/util" + "github.com/containers/storage" + "github.com/docker/distribution/reference" "github.com/gorilla/schema" "github.com/pkg/errors" ) +// NormalizeToDockerHub normalizes the specified nameOrID to Docker Hub if the +// request is for the compat API and if containers.conf set the specific mode. +// If nameOrID is a (short) ID for a local image, the full ID will be returned. +func NormalizeToDockerHub(r *http.Request, nameOrID string) (string, error) { + if IsLibpodRequest(r) || !util.DefaultContainerConfig().Engine.CompatAPIEnforceDockerHub { + return nameOrID, nil + } + + // Try to lookup the input to figure out if it was an ID or not. + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + img, _, err := runtime.LibimageRuntime().LookupImage(nameOrID, nil) + if err != nil { + if errors.Cause(err) != storage.ErrImageUnknown { + return "", fmt.Errorf("normalizing name for compat API: %v", err) + } + } else if strings.HasPrefix(img.ID(), nameOrID) { + return img.ID(), nil + } + + // No ID, so we can normalize. + named, err := reference.ParseNormalizedNamed(nameOrID) + if err != nil { + return "", fmt.Errorf("normalizing name for compat API: %v", err) + } + + return named.String(), nil +} + +// PossiblyEnforceDockerHub sets fields in the system context to enforce +// resolving short names to Docker Hub if the request is for the compat API and +// if containers.conf set the specific mode. +func PossiblyEnforceDockerHub(r *http.Request, sys *types.SystemContext) { + if IsLibpodRequest(r) || !util.DefaultContainerConfig().Engine.CompatAPIEnforceDockerHub { + return + } + sys.PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub = true +} + // IsRegistryReference checks if the specified name points to the "docker://" // transport. If it points to no supported transport, we'll assume a // non-transport reference pointing to an image (e.g., "fedora:latest"). @@ -35,13 +77,13 @@ func IsRegistryReference(name string) error { // `types.ImageReference` and enforces it to refer to a // containers-storage-transport reference. func ParseStorageReference(name string) (types.ImageReference, error) { - storagePrefix := fmt.Sprintf("%s:", storage.Transport.Name()) + storagePrefix := storageTransport.Transport.Name() imageRef, err := alltransports.ParseImageName(name) if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { return nil, errors.Errorf("reference %q must be a storage reference", name) } else if err != nil { origErr := err - imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", storagePrefix, name)) + imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s:%s", storagePrefix, name)) if err != nil { return nil, errors.Wrapf(origErr, "reference %q must be a storage reference", name) } diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 107b10014..be6e5ab55 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "context" "encoding/json" + "fmt" "io" "io/ioutil" "net/http" @@ -382,6 +383,59 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } params.Set("dockerfile", string(cFileJSON)) } + + // build secrets are usually absolute host path or relative to context dir on host + // in any case move secret to current context and ship the tar. + if secrets := options.CommonBuildOpts.Secrets; len(secrets) > 0 { + secretsForRemote := []string{} + + for _, secret := range secrets { + secretOpt := strings.Split(secret, ",") + if len(secretOpt) > 0 { + modifiedOpt := []string{} + for _, token := range secretOpt { + arr := strings.SplitN(token, "=", 2) + if len(arr) > 1 { + if arr[0] == "src" { + // read specified secret into a tmp file + // move tmp file to tar and change secret source to relative tmp file + tmpSecretFile, err := ioutil.TempFile(options.ContextDirectory, "podman-build-secret") + if err != nil { + return nil, err + } + defer os.Remove(tmpSecretFile.Name()) // clean up + defer tmpSecretFile.Close() + srcSecretFile, err := os.Open(arr[1]) + if err != nil { + return nil, err + } + defer srcSecretFile.Close() + _, err = io.Copy(tmpSecretFile, srcSecretFile) + if err != nil { + return nil, err + } + + //add tmp file to context dir + tarContent = append(tarContent, tmpSecretFile.Name()) + + modifiedSrc := fmt.Sprintf("src=%s", filepath.Base(tmpSecretFile.Name())) + modifiedOpt = append(modifiedOpt, modifiedSrc) + } else { + modifiedOpt = append(modifiedOpt, token) + } + } + } + secretsForRemote = append(secretsForRemote, strings.Join(modifiedOpt[:], ",")) + } + } + + c, err := jsoniter.MarshalToString(secretsForRemote) + if err != nil { + return nil, err + } + params.Add("secrets", c) + } + tarfile, err := nTar(append(excludes, dontexcludes...), tarContent...) if err != nil { logrus.Errorf("Cannot tar container entries %v error: %v", tarContent, err) diff --git a/pkg/bindings/test/attach_test.go b/pkg/bindings/test/attach_test.go index 5c3ec48e4..c78836cb3 100644 --- a/pkg/bindings/test/attach_test.go +++ b/pkg/bindings/test/attach_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "bytes" diff --git a/pkg/bindings/test/auth_test.go b/pkg/bindings/test/auth_test.go index d48a3eff7..26690c46a 100644 --- a/pkg/bindings/test/auth_test.go +++ b/pkg/bindings/test/auth_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "io/ioutil" diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 233666a48..76649f628 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" @@ -51,7 +51,7 @@ var ( shortName: "busybox", tarballName: "busybox.tar", } - CACHE_IMAGES = []testImage{alpine, busybox} + CACHE_IMAGES = []testImage{alpine, busybox} //nolint:golint,stylecheck ) type bindingTest struct { diff --git a/pkg/bindings/test/connection_test.go b/pkg/bindings/test/connection_test.go index 561cf32b5..84a047bc9 100644 --- a/pkg/bindings/test/connection_test.go +++ b/pkg/bindings/test/connection_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 0f535bc31..b6c06756b 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "net/http" @@ -103,6 +103,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) // Pause by name err = containers.Pause(bt.conn, name, nil) + Expect(err).To(BeNil(), "error from containers.Pause()") //paused := "paused" //_, err = containers.Wait(bt.conn, cid, &paused) //Expect(err).To(BeNil()) diff --git a/pkg/bindings/test/create_test.go b/pkg/bindings/test/create_test.go index 3c83aac02..f70ba7248 100644 --- a/pkg/bindings/test/create_test.go +++ b/pkg/bindings/test/create_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "time" diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go index c10452eaf..d2f9b121a 100644 --- a/pkg/bindings/test/exec_test.go +++ b/pkg/bindings/test/exec_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "time" diff --git a/pkg/bindings/test/generator_test.go b/pkg/bindings/test/generator_test.go index d04cc10f9..ab0c49713 100644 --- a/pkg/bindings/test/generator_test.go +++ b/pkg/bindings/test/generator_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "github.com/containers/podman/v3/pkg/bindings/containers" diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index aa8ff0537..8489e6ff1 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "net/http" @@ -89,6 +89,10 @@ var _ = Describe("Podman images", func() { response, errs := images.Remove(bt.conn, []string{"foobar5000"}, nil) Expect(len(errs)).To(BeNumerically(">", 0)) code, _ := bindings.CheckResponseCode(errs[0]) + // FIXME FIXME FIXME: #12441: THIS IS BROKEN + // FIXME FIXME FIXME: we get msg: "foobar5000: image not known" + // FIXME FIXME FIXME: ...with no ResponseCode + Expect(code).To(BeNumerically("==", -1)) // Remove an image by name, validate image is removed and error is nil inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil) @@ -99,6 +103,7 @@ var _ = Describe("Podman images", func() { Expect(inspectData.ID).To(Equal(response.Deleted[0])) inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a container with alpine image var top string = "top" @@ -113,6 +118,9 @@ var _ = Describe("Podman images", func() { // deleting hence image cannot be deleted until the container is deleted. response, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil) code, _ = bindings.CheckResponseCode(errs[0]) + // FIXME FIXME FIXME: #12441: another invalid error + // FIXME FIXME FIXME: this time msg="Image used by SHA: ..." + Expect(code).To(BeNumerically("==", -1)) // Removing the image "alpine" where force = true options := new(images.RemoveOptions).WithForce(true) @@ -122,10 +130,12 @@ var _ = Describe("Podman images", func() { // is gone as well. _, err = containers.Inspect(bt.conn, "top", nil) code, _ = bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Now make sure both images are gone. inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) inspectData, err = images.GetImage(bt.conn, alpine.shortName, nil) code, _ = bindings.CheckResponseCode(err) @@ -339,6 +349,7 @@ var _ = Describe("Podman images", func() { // Search with a fqdn reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil) + Expect(err).To(BeNil(), "Error in images.Search()") Expect(len(reports)).To(BeNumerically(">=", 1)) }) diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go index f61e8c370..f643a2c28 100644 --- a/pkg/bindings/test/info_test.go +++ b/pkg/bindings/test/info_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "runtime" diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go index 4d8ca74bf..e65632057 100644 --- a/pkg/bindings/test/manifests_test.go +++ b/pkg/bindings/test/manifests_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "net/http" diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go index 85ceeb998..d95862f6f 100644 --- a/pkg/bindings/test/networks_test.go +++ b/pkg/bindings/test/networks_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 879d4d00d..0a4261ea2 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "fmt" @@ -79,6 +79,7 @@ var _ = Describe("Podman pods", func() { var newpod2 string = "newpod2" bt.Podcreate(&newpod2) podSummary, err = pods.List(bt.conn, nil) + Expect(err).To(BeNil(), "Error from pods.List") Expect(len(podSummary)).To(Equal(2)) var names []string for _, i := range podSummary { @@ -106,6 +107,7 @@ var _ = Describe("Podman pods", func() { options := new(pods.ListOptions).WithFilters(filters) filteredPods, err := pods.List(bt.conn, options) Expect(err).ToNot(BeNil()) + Expect(len(filteredPods)).To(Equal(0), "len(filteredPods)") code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -301,6 +303,7 @@ var _ = Describe("Podman pods", func() { // No pods pruned since no pod in exited state pruneResponse, err := pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) + Expect(len(pruneResponse)).To(Equal(0), "len(pruneResponse)") podSummary, err := pods.List(bt.conn, nil) Expect(err).To(BeNil()) Expect(len(podSummary)).To(Equal(2)) @@ -317,6 +320,7 @@ var _ = Describe("Podman pods", func() { Expect(response.State).To(Equal(define.PodStateExited)) pruneResponse, err = pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) + Expect(len(pruneResponse)).To(Equal(1), "len(pruneResponse)") // Validate status and record pod id of pod to be pruned Expect(response.State).To(Equal(define.PodStateExited)) podID := response.ID diff --git a/pkg/bindings/test/resource_test.go b/pkg/bindings/test/resource_test.go index b12d1ccd6..19ac33eb2 100644 --- a/pkg/bindings/test/resource_test.go +++ b/pkg/bindings/test/resource_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" diff --git a/pkg/bindings/test/secrets_test.go b/pkg/bindings/test/secrets_test.go index 5edb37f18..52c964556 100644 --- a/pkg/bindings/test/secrets_test.go +++ b/pkg/bindings/test/secrets_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index aecc5db81..4549a14c9 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "sync" diff --git a/pkg/bindings/test/test_suite_test.go b/pkg/bindings/test/test_suite_test.go index d2c2c7838..8fb46c205 100644 --- a/pkg/bindings/test/test_suite_test.go +++ b/pkg/bindings/test/test_suite_test.go @@ -1,4 +1,4 @@ -package test_bindings_test +package bindings_test import ( "testing" diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go index 14bda114e..43ef54889 100644 --- a/pkg/bindings/test/volumes_test.go +++ b/pkg/bindings/test/volumes_test.go @@ -1,4 +1,4 @@ -package test_bindings +package bindings_test import ( "context" 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/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index fde57972f..2feb9d7ad 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -270,7 +270,10 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, defer func() { _ = os.Remove(f.Name()) }() } default: - f, err = os.Create(opts.Output) + // This code was added to allow for opening stdout replacing + // os.Create(opts.Output) which was attempting to open the file + // for read/write which fails on Darwin platforms + f, err = os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) } if err != nil { return err diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index e19940b22..5c465d37d 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -89,7 +89,7 @@ Type=oneshot RemainAfterExit=yes ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s' [Install] -RequiredBy=multi-user.target +RequiredBy=default.target ` deMoby := `[Unit] Description=Remove moby-engine @@ -106,7 +106,7 @@ ExecStart=/usr/bin/rpm-ostree ex apply-live --allow-replacement ExecStartPost=/bin/touch /var/lib/%N.stamp [Install] -WantedBy=multi-user.target +WantedBy=default.target ` _ = ready ignSystemd := Systemd{ 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/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 2fdec5fb1..d0c94123d 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -134,7 +134,7 @@ NotifyAccess={{{{.NotifyAccess}}}} {{{{- end}}}} [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` // ContainerUnit generates a systemd unit for the specified container. Based diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index eab2c2e67..33b09005c 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -62,7 +62,7 @@ PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodID := serviceInfo + headerInfo + goodIDContent goodIDNoHeaderInfo := serviceInfo + goodIDContent @@ -88,7 +88,7 @@ PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameBoundTo := `# container-foobar.service @@ -114,7 +114,7 @@ PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodWithNameAndGeneric := `# jadda-jadda.service @@ -139,7 +139,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodWithNameAndSdnotify := `# jadda-jadda.service @@ -164,7 +164,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodWithExplicitShortDetachParam := `# jadda-jadda.service @@ -189,7 +189,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameNewWithPodFile := `# jadda-jadda.service @@ -214,7 +214,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameNewDetach := `# jadda-jadda.service @@ -239,7 +239,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service @@ -264,7 +264,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` genGoodNewDetach := func(detachparam string) string { @@ -292,7 +292,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` return goodNewDetach } @@ -319,7 +319,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewRootFlags := `# jadda-jadda.service @@ -344,7 +344,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodContainerCreate := `# jadda-jadda.service @@ -369,7 +369,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithJournaldTag := `# jadda-jadda.service @@ -394,7 +394,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithSpecialChars := `# jadda-jadda.service @@ -419,7 +419,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithIDFiles := `# jadda-jadda.service @@ -444,7 +444,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithPodIDFiles := `# jadda-jadda.service @@ -469,7 +469,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithEnvar := `# jadda-jadda.service @@ -495,7 +495,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithRestartPolicy := `# jadda-jadda.service @@ -521,7 +521,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` templateGood := `# container-foo@.service @@ -547,7 +547,7 @@ Type=notify NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` tests := []struct { name string diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index f4cc31c8e..48252c737 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -103,7 +103,7 @@ PIDFile={{{{.PIDFile}}}} Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` // PodUnits generates systemd units for the specified pod and its containers. diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index c565a30ed..612908991 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -62,7 +62,7 @@ PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGood := serviceInfo + headerInfo + podContent podGoodNoHeaderInfo := serviceInfo + podContent @@ -92,7 +92,7 @@ PIDFile=%t/pod-123abc.pid Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGoodNamedNewWithRootArgs := `# pod-123abc.service @@ -120,7 +120,7 @@ PIDFile=%t/pod-123abc.pid Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGoodNamedNewWithReplaceFalse := `# pod-123abc.service @@ -148,7 +148,7 @@ PIDFile=%t/pod-123abc.pid Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podNewLabelWithCurlyBraces := `# pod-123abc.service @@ -176,7 +176,7 @@ PIDFile=%t/pod-123abc.pid Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` tests := []struct { diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index 144b83194..3a5d5c096 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -47,8 +47,7 @@ t POST "images/localhost:5000/myrepo/push?tlsVerify=false&tag=mytag" 200 t POST "libpod/images/$iid/untag?repo=localhost:5000/myrepo&tag=mytag" 201 # Try to push non-existing image -t POST "images/localhost:5000/idonotexist/push?tlsVerify=false" 200 -jq -re 'select(.errorDetail)' <<<"$output" &>/dev/null || echo -e "${red}not ok: error message not found in output${nc}" 1>&2 +t POST "images/localhost:5000/idonotexist/push?tlsVerify=false" 404 t GET libpod/images/$IMAGE/json 200 \ .RepoTags[-1]=$IMAGE diff --git a/test/apiv2/70-short-names.at b/test/apiv2/70-short-names.at new file mode 100644 index 000000000..a5087c115 --- /dev/null +++ b/test/apiv2/70-short-names.at @@ -0,0 +1,148 @@ +# -*- sh -*- +# +# Tests for exercising short-name resolution in the compat API. +# + +# Pull the libpod/quay image which is used in all tests below. +t POST "images/create?fromImage=quay.io/libpod/alpine:latest" 200 .error~null .status~".*Download complete.*" + + +########## TAG + +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t DELETE "images/foo" 200 + + +########## BUILD + +function test_build { + from=$1 + tag=$2 + fqn=$3 + + TMPD=$(mktemp -d podman-apiv2-test.build.XXXXXXXX) + CONTAINERFILE_TAR="${TMPD}/containerfile.tar" + cat > $TMPD/containerfile << EOF +FROM $from +RUN touch /foo +EOF + tar --format=posix -C $TMPD -cvf ${CONTAINERFILE_TAR} containerfile &> /dev/null + + curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/x-tar" \ + --dump-header "${TMPD}/headers.txt" \ + -o "${TMPD}/response.txt" \ + "http://$HOST:$PORT/build?dockerfile=containerfile&t=$tag" &> /dev/null + + if ! grep -q '200 OK' "${TMPD}/headers.txt"; then + cat "${TMPD}/headers.txt" + cat "${TMPD}/response.txt" + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/x-tar)" + exit 1 + fi + + rm -rf $TMPD + t DELETE "images/$fqn" 200 +} + +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +test_build foo bar "docker.io/library/bar:latest" +t DELETE "images/foo" 200 + + +########## TAG + +# Looking up 'alpine' will fail as it gets normalized to docker.io. +t POST "images/alpine/tag?repo=foo" 404 .cause="image not known" + +# The libpod endpoint will resolve to it without issues. +t GET "libpod/images/alpine/exists" 204 + +# Now let's tag the image with 'foo'. Remember, it will be normalized to +# docker.io/library/foo. +t GET "libpod/images/docker.io/library/foo/exists" 404 +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t GET "libpod/images/docker.io/library/foo/exists" 204 + + +########## REMOVE + +t DELETE "images/alpine" 404 .cause="image not known" # fails since docker.io/library/alpine does not exist +t DELETE "images/foo" 200 # removes the previously tagged image + + +########## GET + +# Same procedure as above but with the /get endpoint. +t GET "images/alpine/get" 404 .cause="image not known" +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t GET "images/foo/get" 200 '[POSIX tar archive]' +t DELETE "images/foo" 200 + + +########## HISTORY + +t GET "images/alpine/history" 404 .cause="image not known" +t GET "images/quay.io/libpod/alpine/history" 200 +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t GET "libpod/images/foo/history" 200 +t DELETE "images/foo" 200 + + +########## PUSH + +t POST "images/alpine/push?destination=localhost:9999/do/not:exist" 404 .cause="image not known" +t POST "images/quay.io/libpod/alpine/push?destination=localhost:9999/do/not:exist" 200 # Error is in the response +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t POST "images/foo/push?destination=localhost:9999/do/not:exist" 200 # Error is in the response +t DELETE "images/foo" 200 + + +########## CREATE A CONTAINER + +t POST "containers/create" Image=alpine 404 .cause="image not known" +t POST "containers/create" Image=quay.io/libpod/alpine:latest 201 +cid=$(jq -r '.Id' <<<"$output") +t POST "images/quay.io/libpod/alpine/tag?repo=foo" 201 +t POST "containers/create" Image=foo 201 +cid=$(jq -r '.Id' <<<"$output") +t DELETE "images/foo" 200 +t DELETE "containers/$cid" 204 + +########## COMMIT CONTAINER + +t POST "containers/create" Image=quay.io/libpod/alpine:latest 201 +cid=$(jq -r '.Id' <<<"$output") +t GET "images/alpine/get" 404 .cause="image not known" +t POST "commit?container=$cid&repo=foo&tag=tag" 201 +t GET "images/foo/get" 404 .cause="image not known" +t GET "images/foo:tag/get" 200 +t DELETE "images/docker.io/library/foo:tag" 200 +t DELETE "containers/$cid" 204 + + +######### SMOKE TESTS WITHOUT DOCKER.IO ENFORCEMENT + +# Note that we need to restart the service with a custom containers.conf to +# disable the docker.io enforcement. + +stop_service +CONTAINERS_CONF=$(pwd)/test/apiv2/containers.conf start_service + +t POST "images/create?fromImage=quay.io/libpod/alpine:latest" 200 .error~null .status~".*Download complete.*" +t POST "images/alpine/tag?repo=foo" 201 +t GET "images/localhost/foo:latest/get" 200 +t DELETE "images/foo" 200 +t GET "images/alpine/history" 200 +t POST "images/alpine/push?destination=localhost:9999/do/not:exist" 200 # Error is in the response +t POST "containers/create" Image=alpine 201 +cid=$(jq -r '.Id' <<<"$output") +t POST "commit?container=$cid&repo=foo&tag=tag" 201 +t DELETE "images/localhost/foo:tag" 200 +t DELETE "containers/$cid" 204 + +test_build alpine bar "localhost/bar:latest" + + +stop_service +start_service diff --git a/test/apiv2/containers.conf b/test/apiv2/containers.conf new file mode 100644 index 000000000..24762192f --- /dev/null +++ b/test/apiv2/containers.conf @@ -0,0 +1,8 @@ +# This containers.conf file is used to test enforcing short-name resolution to +# docker.io for Podman's *compat* API. By default, the compat API defaults to +# resolving to docker.io only. The behavior can be altered by configuring the +# containers.conf as done below in which case short names are subject to aliases, +# "localhost/" and the unqualified-search registries. + +[engine] +compat_api_enforce_docker_hub=false diff --git a/test/e2e/build/Dockerfile.with-multiple-secret b/test/e2e/build/Dockerfile.with-multiple-secret new file mode 100644 index 000000000..f3478914f --- /dev/null +++ b/test/e2e/build/Dockerfile.with-multiple-secret @@ -0,0 +1,3 @@ +FROM alpine +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret +RUN --mount=type=secret,id=mysecret2 cat /run/secrets/mysecret2 diff --git a/test/e2e/build/Dockerfile.with-secret b/test/e2e/build/Dockerfile.with-secret new file mode 100644 index 000000000..920663a92 --- /dev/null +++ b/test/e2e/build/Dockerfile.with-secret @@ -0,0 +1,2 @@ +FROM alpine +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build/Dockerfile.with-secret-verify-leak b/test/e2e/build/Dockerfile.with-secret-verify-leak new file mode 100644 index 000000000..0957ac6a6 --- /dev/null +++ b/test/e2e/build/Dockerfile.with-secret-verify-leak @@ -0,0 +1,3 @@ +FROM alpine +COPY * / +RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret diff --git a/test/e2e/build/anothersecret.txt b/test/e2e/build/anothersecret.txt new file mode 100644 index 000000000..bc5fdbd32 --- /dev/null +++ b/test/e2e/build/anothersecret.txt @@ -0,0 +1 @@ +anothersecret diff --git a/test/e2e/build/secret.txt b/test/e2e/build/secret.txt new file mode 100644 index 000000000..d9106c0af --- /dev/null +++ b/test/e2e/build/secret.txt @@ -0,0 +1 @@ +somesecret diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go index 420ed929f..5e93f9078 100644 --- a/test/e2e/build_test.go +++ b/test/e2e/build_test.go @@ -59,6 +59,45 @@ var _ = Describe("Podman build", func() { Expect(session).Should(Exit(0)) }) + It("podman build with a secret from file", func() { + session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret", "-t", "secret-test", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("somesecret")) + + session = podmanTest.Podman([]string{"rmi", "secret-test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman build with multiple secrets from files", func() { + session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-multiple-secret", "-t", "multiple-secret-test", "--secret", "id=mysecret,src=build/secret.txt", "--secret", "id=mysecret2,src=build/anothersecret.txt", "build/"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("somesecret")) + Expect(session.OutputToString()).To(ContainSubstring("anothersecret")) + + session = podmanTest.Podman([]string{"rmi", "multiple-secret-test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman build with a secret from file and verify if secret file is not leaked into image", func() { + session := podmanTest.Podman([]string{"build", "-f", "build/Dockerfile.with-secret-verify-leak", "-t", "secret-test-leak", "--secret", "id=mysecret,src=build/secret.txt", "build/"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("somesecret")) + + session = podmanTest.Podman([]string{"run", "--rm", "secret-test-leak", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Not(ContainSubstring("podman-build-secret"))) + + session = podmanTest.Podman([]string{"rmi", "secret-test-leak"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman build with logfile", func() { logfile := filepath.Join(podmanTest.TempDir, "logfile") session := podmanTest.Podman([]string{"build", "--pull-never", "--tag", "test", "--logfile", logfile, "build/basicalpine"}) @@ -388,13 +427,10 @@ subdir**` session := podmanTest.Podman([]string{"build", "-t", "test", "."}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - ok, _ := session.GrepString("/testfilter/dummy1") - Expect(ok).NotTo(BeTrue()) - Expect(session.OutputToString()).To(ContainSubstring("/testfilter/dummy2")) - ok, _ = session.GrepString("/testfilter/subdir") - Expect(ok).NotTo(BeTrue()) //.dockerignore filters both subdir and inside subdir - ok, _ = session.GrepString("/testfilter/subdir/dummy3") - Expect(ok).NotTo(BeTrue()) + output := session.OutputToString() + Expect(output).To(ContainSubstring("/testfilter/dummy2")) + Expect(output).NotTo(ContainSubstring("/testfilter/dummy1")) + Expect(output).NotTo(ContainSubstring("/testfilter/subdir")) }) It("podman remote test context dir contains empty dirs and symlinks", func() { diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index cc827a453..5a2640f33 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -102,7 +102,7 @@ var _ = Describe("Podman checkpoint", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) inspectOut := inspect.InspectContainerToJSON() - Expect(inspectOut[0].State.Checkpointed).To(BeTrue()) + Expect(inspectOut[0].State.Checkpointed).To(BeTrue(), ".State.Checkpointed") result = podmanTest.Podman([]string{"container", "restore", cid}) result.WaitWithDefaultTimeout() @@ -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() @@ -1019,6 +1019,9 @@ var _ = Describe("Podman checkpoint", func() { "podman checkpoint and restore container out of and into pod (%s)", share, ) + + share := share // copy into local scope, for use inside function + It(testName, func() { if !criu.CheckForCriu(criu.PodCriuVersion) { Skip("CRIU is missing or too old.") @@ -1294,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 20e1360de..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()) + 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()) + 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/common_test.go b/test/e2e/common_test.go index 6180343a7..63cb4f091 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -24,7 +24,6 @@ import ( "github.com/containers/storage/pkg/reexec" "github.com/containers/storage/pkg/stringid" jsoniter "github.com/json-iterator/go" - "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -33,16 +32,14 @@ import ( ) var ( - PODMAN_BINARY string - CONMON_BINARY string - CNI_CONFIG_DIR string - RUNC_BINARY string - INTEGRATION_ROOT string - CGROUP_MANAGER = "systemd" - ARTIFACT_DIR = "/tmp/.artifacts" - RESTORE_IMAGES = []string{ALPINE, BB, nginx} + //lint:ignore ST1003 + PODMAN_BINARY string //nolint:golint,stylecheck + INTEGRATION_ROOT string //nolint:golint,stylecheck + CGROUP_MANAGER = "systemd" //nolint:golint,stylecheck + ARTIFACT_DIR = "/tmp/.artifacts" //nolint:golint,stylecheck + RESTORE_IMAGES = []string{ALPINE, BB, nginx} //nolint:golint,stylecheck defaultWaitTimeout = 90 - CGROUPSV2, _ = cgroups.IsCgroup2UnifiedMode() + CGROUPSV2, _ = cgroups.IsCgroup2UnifiedMode() //nolint:golint,stylecheck ) // PodmanTestIntegration struct for command line options @@ -74,8 +71,6 @@ type testResult struct { length float64 } -var noCache = "Cannot run nocache with remote" - type testResultsSorted []testResult func (a testResultsSorted) Len() int { return len(a) } @@ -475,7 +470,7 @@ func (p *PodmanTestIntegration) PodmanPID(args []string) (*PodmanSessionIntegrat if err != nil { Fail(fmt.Sprintf("unable to run podman command: %s", strings.Join(podmanOptions, " "))) } - podmanSession := &PodmanSession{session} + podmanSession := &PodmanSession{Session: session} return &PodmanSessionIntegration{podmanSession}, command.Process.Pid } @@ -597,9 +592,9 @@ func (p *PodmanTestIntegration) RunHealthCheck(cid string) error { return errors.Errorf("unable to detect %s as running", cid) } -func (p *PodmanTestIntegration) CreateSeccompJson(in []byte) (string, error) { +func (p *PodmanTestIntegration) CreateSeccompJSON(in []byte) (string, error) { jsonFile := filepath.Join(p.TempDir, "seccomp.json") - err := WriteJsonFile(in, jsonFile) + err := WriteJSONFile(in, jsonFile) if err != nil { return "", err } @@ -622,14 +617,14 @@ func SkipIfRootlessCgroupsV1(reason string) { func SkipIfRootless(reason string) { checkReason(reason) if os.Geteuid() != 0 { - ginkgo.Skip("[rootless]: " + reason) + Skip("[rootless]: " + reason) } } func SkipIfNotRootless(reason string) { checkReason(reason) if os.Geteuid() == 0 { - ginkgo.Skip("[notRootless]: " + reason) + Skip("[notRootless]: " + reason) } } @@ -640,7 +635,7 @@ func SkipIfSystemdNotRunning(reason string) { err := cmd.Run() if err != nil { if _, ok := err.(*exec.Error); ok { - ginkgo.Skip("[notSystemd]: not running " + reason) + Skip("[notSystemd]: not running " + reason) } Expect(err).ToNot(HaveOccurred()) } @@ -649,14 +644,14 @@ func SkipIfSystemdNotRunning(reason string) { func SkipIfNotSystemd(manager, reason string) { checkReason(reason) if manager != "systemd" { - ginkgo.Skip("[notSystemd]: " + reason) + Skip("[notSystemd]: " + reason) } } func SkipIfNotFedora() { info := GetHostDistributionInfo() if info.Distribution != "fedora" { - ginkgo.Skip("Test can only run on Fedora") + Skip("Test can only run on Fedora") } } @@ -684,10 +679,7 @@ func SkipIfCgroupV2(reason string) { func isContainerized() bool { // This is set to "podman" by podman automatically - if os.Getenv("container") != "" { - return true - } - return false + return os.Getenv("container") != "" } func SkipIfContainerized(reason string) { @@ -702,7 +694,7 @@ func SkipIfRemote(reason string) { if !IsRemote() { return } - ginkgo.Skip("[remote]: " + reason) + Skip("[remote]: " + reason) } // SkipIfInContainer skips a test if the test is run inside a container @@ -872,10 +864,10 @@ func (p *PodmanTestIntegration) removeCNINetwork(name string) { Expect(session.ExitCode()).To(BeNumerically("<=", 1), "Exit code must be 0 or 1") } -func (p *PodmanSessionIntegration) jq(jqCommand string) (string, error) { +func (s *PodmanSessionIntegration) jq(jqCommand string) (string, error) { var out bytes.Buffer cmd := exec.Command("jq", jqCommand) - cmd.Stdin = strings.NewReader(p.OutputToString()) + cmd.Stdin = strings.NewReader(s.OutputToString()) cmd.Stdout = &out err := cmd.Run() return strings.TrimRight(out.String(), "\n"), err diff --git a/test/e2e/config_amd64.go b/test/e2e/config_amd64.go index 3607bdc30..9293fdd44 100644 --- a/test/e2e/config_amd64.go +++ b/test/e2e/config_amd64.go @@ -1,16 +1,16 @@ package integration var ( - STORAGE_FS = "vfs" - STORAGE_OPTIONS = "--storage-driver vfs" - ROOTLESS_STORAGE_FS = "vfs" - ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs" - CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels, healthcheck, ubi_init, ubi_minimal, fedoraToolbox} + STORAGE_FS = "vfs" //nolint:golint,stylecheck + STORAGE_OPTIONS = "--storage-driver vfs" //nolint:golint,stylecheck + ROOTLESS_STORAGE_FS = "vfs" //nolint:golint,stylecheck + ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs" //nolint:golint,stylecheck + CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels, healthcheck, UBI_INIT, UBI_MINIMAL, fedoraToolbox} //nolint:golint,stylecheck nginx = "quay.io/libpod/alpine_nginx:latest" - BB_GLIBC = "docker.io/library/busybox:glibc" + BB_GLIBC = "docker.io/library/busybox:glibc" //nolint:golint,stylecheck registry = "quay.io/libpod/registry:2.6" labels = "quay.io/libpod/alpine_labels:latest" - ubi_minimal = "registry.access.redhat.com/ubi8-minimal" - ubi_init = "registry.access.redhat.com/ubi8-init" + UBI_MINIMAL = "registry.access.redhat.com/ubi8-minimal" //nolint:golint,stylecheck + UBI_INIT = "registry.access.redhat.com/ubi8-init" //nolint:golint,stylecheck cirros = "quay.io/libpod/cirros:latest" ) diff --git a/test/e2e/container_create_volume_test.go b/test/e2e/container_create_volume_test.go index 001698239..0dac96a5e 100644 --- a/test/e2e/container_create_volume_test.go +++ b/test/e2e/container_create_volume_test.go @@ -28,7 +28,7 @@ VOLUME %s/`, data, dest, dest) func createContainersConfFile(pTest *PodmanTestIntegration) { configPath := filepath.Join(pTest.TempDir, "containers.conf") - containersConf := []byte(fmt.Sprintf("[containers]\nprepare_volume_on_create = true\n")) + containersConf := []byte("[containers]\nprepare_volume_on_create = true\n") err := ioutil.WriteFile(configPath, containersConf, os.ModePerm) Expect(err).To(BeNil()) diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 6a760da17..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() { @@ -407,7 +407,7 @@ var _ = Describe("Podman run", func() { configPath := filepath.Join(podmanTest.TempDir, "containers.conf") os.Setenv("CONTAINERS_CONF", configPath) - containersConf := []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"/foobar\"")) + containersConf := []byte("[engine]\nimage_copy_tmp_dir=\"/foobar\"") err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) Expect(err).To(BeNil()) @@ -420,7 +420,7 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/foobar")) - containersConf = []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"storage\"")) + containersConf = []byte("[engine]\nimage_copy_tmp_dir=\"storage\"") err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) Expect(err).To(BeNil()) if IsRemote() { @@ -432,7 +432,7 @@ var _ = Describe("Podman run", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("containers/storage/tmp")) - containersConf = []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"storage1\"")) + containersConf = []byte("[engine]\nimage_copy_tmp_dir=\"storage1\"") err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) Expect(err).To(BeNil()) if IsRemote() { diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 216432216..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()) - Expect(value).To(Equal("WORLD")) + Expect(data[0].Config.Annotations).To(HaveKeyWithValue("HELLO", "WORLD")) }) It("podman create --entrypoint command", func() { @@ -202,7 +200,7 @@ var _ = Describe("Podman create", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test ro")) - session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test_shared", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,shared", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"}) + session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test_shared", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,shared", mountPath), ALPINE, "awk", `$5 == "/create/test" { print $6 " " $7}`, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test_shared"}) @@ -211,10 +209,8 @@ var _ = Describe("Podman create", func() { session = podmanTest.Podman([]string{"logs", "test_shared"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - found, matches := session.GrepString("/create/test") - 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")) mountPath = filepath.Join(podmanTest.TempDir, "scratchpad") os.Mkdir(mountPath, 0755) @@ -263,7 +259,7 @@ var _ = Describe("Podman create", func() { session = podmanTest.Podman([]string{"pod", "inspect", podName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) podData := session.InspectPodToJSON() // Finally we can create a container with --pod-id-file and do @@ -386,12 +382,10 @@ var _ = Describe("Podman create", func() { inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() data := inspect.InspectContainerToJSON() - Expect(len(data)).To(Equal(1)) + 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()) - _, ok2 := data[0].Config.Labels["TESTKEY2"] - Expect(ok2).To(BeTrue()) + Expect(data[0].Config.Labels).To(HaveKey("TESTKEY1")) + Expect(data[0].Config.Labels).To(HaveKey("TESTKEY2")) }) It("podman create with set label", func() { @@ -406,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()) - Expect(val1).To(Equal("value1")) - val2, ok2 := data[0].Config.Labels["TESTKEY2"] - Expect(ok2).To(BeTrue()) - 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/diff_test.go b/test/e2e/diff_test.go index 71696f5b6..80647c6f5 100644 --- a/test/e2e/diff_test.go +++ b/test/e2e/diff_test.go @@ -53,7 +53,7 @@ var _ = Describe("Podman diff", func() { session := podmanTest.Podman([]string{"diff", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman diff container and committed image", func() { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index a148025e5..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()) - 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/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index e93482535..1cffdc62e 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -420,7 +420,7 @@ var _ = Describe("Podman generate systemd", func() { session := podmanTest.Podman([]string{"generate", "systemd", "--format", "json", "foo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman generate systemd --new create command with double curly braces", func() { diff --git a/test/e2e/history_test.go b/test/e2e/history_test.go index 0f0f6d38a..1f8faa6c2 100644 --- a/test/e2e/history_test.go +++ b/test/e2e/history_test.go @@ -89,6 +89,6 @@ var _ = Describe("Podman history", func() { session := podmanTest.Podman([]string{"history", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) }) diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go index 63276e57f..6c6c85bc3 100644 --- a/test/e2e/image_scp_test.go +++ b/test/e2e/image_scp_test.go @@ -62,7 +62,7 @@ var _ = Describe("podman image scp", func() { }) It("podman image scp root to rootless transfer", func() { - SkipIfNotRootless("this is a rootless only test, transfering from root to rootless using PodmanAsUser") + SkipIfNotRootless("this is a rootless only test, transferring from root to rootless using PodmanAsUser") if IsRemote() { Skip("this test is only for non-remote") } diff --git a/test/e2e/image_sign_test.go b/test/e2e/image_sign_test.go index 6b87c9edd..09e156e02 100644 --- a/test/e2e/image_sign_test.go +++ b/test/e2e/image_sign_test.go @@ -73,6 +73,6 @@ var _ = Describe("Podman image sign", func() { Expect(session).Should(Exit(0)) fInfos, err := ioutil.ReadDir(filepath.Join(sigDir, "library")) Expect(err).To(BeNil()) - Expect(len(fInfos) > 1).To(BeTrue()) + Expect(len(fInfos)).To(BeNumerically(">", 1), "len(fInfos)") }) }) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 3ed8ef462..9cc04e6b0 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -94,14 +94,14 @@ var _ = Describe("Podman images", func() { session := podmanTest.Podman([]string{"images", "--format=json", "not-existing-image"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman images in JSON format", func() { session := podmanTest.Podman([]string{"images", "--format=json"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman images in GO template format", func() { @@ -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. @@ -254,7 +253,7 @@ WORKDIR /test session := podmanTest.Podman([]string{"inspect", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) imageData := session.InspectImageJSON() result := podmanTest.Podman([]string{"images", "sha256:" + imageData[0].ID}) @@ -266,7 +265,7 @@ WORKDIR /test session := podmanTest.Podman([]string{"image", "inspect", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) imageData := session.InspectImageJSON() result := podmanTest.Podman([]string{"image", "ls", fmt.Sprintf("sha256:%s", imageData[0].ID)}) diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 52726015c..0f0237adc 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -38,7 +38,7 @@ var _ = Describe("Podman inspect", func() { session := podmanTest.Podman([]string{"inspect", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) imageData := session.InspectImageJSON() Expect(imageData[0].RepoTags[0]).To(Equal("quay.io/libpod/alpine:latest")) }) @@ -314,7 +314,7 @@ var _ = Describe("Podman inspect", func() { inspect := podmanTest.Podman([]string{"inspect", podName}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) }) @@ -328,7 +328,7 @@ var _ = Describe("Podman inspect", func() { inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) }) @@ -343,7 +343,7 @@ var _ = Describe("Podman inspect", func() { inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", "--latest"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) }) @@ -357,14 +357,14 @@ var _ = Describe("Podman inspect", func() { inspect1 := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) inspect1.WaitWithDefaultTimeout() Expect(inspect1).Should(Exit(0)) - Expect(inspect1.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect1.OutputToString()).To(BeValidJSON()) podData := inspect1.InspectPodArrToJSON() infra := podData[0].Containers[0].Name inspect := podmanTest.Podman([]string{"inspect", "--latest"}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) containerData := inspect.InspectContainerToJSON() Expect(containerData[0].Name).To(Equal(infra)) }) @@ -388,7 +388,7 @@ var _ = Describe("Podman inspect", func() { session = podmanTest.Podman([]string{"inspect", volName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman inspect a volume with --format", func() { diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index 1fa29daa1..2ecbd0eab 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -111,7 +111,6 @@ func (p *PodmanTestIntegration) StopRemoteService() { if _, err := remoteSession.Wait(); err != nil { fmt.Fprintf(os.Stderr, "error on remote stop-wait %q", err) } - } else { parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid) pgrep := exec.Command("pgrep", "-P", parentPid) diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go index 030e9f80b..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() { @@ -221,7 +220,7 @@ var _ = Describe("Podman load", func() { result := podmanTest.Podman([]string{"images", "hello:world"}) result.WaitWithDefaultTimeout() - Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) + Expect(result.OutputToString()).To(Not(ContainSubstring("docker"))) Expect(result.OutputToString()).To(ContainSubstring("localhost")) }) @@ -246,7 +245,7 @@ var _ = Describe("Podman load", func() { result := podmanTest.Podman([]string{"images", "hello:latest"}) result.WaitWithDefaultTimeout() - Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) + Expect(result.OutputToString()).To(Not(ContainSubstring("docker"))) Expect(result.OutputToString()).To(ContainSubstring("localhost")) }) @@ -272,7 +271,7 @@ var _ = Describe("Podman load", func() { result := podmanTest.Podman([]string{"images", "load:latest"}) result.WaitWithDefaultTimeout() - Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) + Expect(result.OutputToString()).To(Not(ContainSubstring("docker"))) Expect(result.OutputToString()).To(ContainSubstring("localhost")) }) diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index d901dde5c..2558af0eb 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -20,10 +20,7 @@ func isEventBackendJournald(podmanTest *PodmanTestIntegration) bool { } info := podmanTest.Podman([]string{"info", "--format", "{{.Host.EventLogger}}"}) info.WaitWithDefaultTimeout() - if info.OutputToString() == "journald" { - return true - } - return false + return info.OutputToString() == "journald" } var _ = Describe("Podman logs", func() { diff --git a/test/e2e/mount_test.go b/test/e2e/mount_test.go index 5ecd61097..0555a0749 100644 --- a/test/e2e/mount_test.go +++ b/test/e2e/mount_test.go @@ -78,7 +78,7 @@ var _ = Describe("Podman mount", func() { j := podmanTest.Podman([]string{"mount", "--format=json"}) j.WaitWithDefaultTimeout() Expect(j).Should(Exit(0)) - Expect(j.IsJSONOutputValid()).To(BeTrue()) + Expect(j.OutputToString()).To(BeValidJSON()) j = podmanTest.Podman([]string{"mount", "--format='{{.foobar}}'"}) j.WaitWithDefaultTimeout() @@ -332,7 +332,7 @@ var _ = Describe("Podman mount", func() { j := podmanTest.Podman([]string{"image", "mount", "--format=json"}) j.WaitWithDefaultTimeout() Expect(j).Should(Exit(0)) - Expect(j.IsJSONOutputValid()).To(BeTrue()) + Expect(j.OutputToString()).To(BeValidJSON()) umount := podmanTest.Podman([]string{"image", "umount", fedoraMinimal}) umount.WaitWithDefaultTimeout() diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 953380335..d56cde9db 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "os" - "strings" "time" "github.com/containers/podman/v3/libpod/network/types" @@ -141,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() { @@ -185,7 +184,7 @@ var _ = Describe("Podman network", func() { Expect(session).Should(Exit(0)) }) - rm_func := func(rm string) { + rmFunc := func(rm string) { It(fmt.Sprintf("podman network %s no args", rm), func() { session := podmanTest.Podman([]string{"network", rm}) session.WaitWithDefaultTimeout() @@ -209,12 +208,12 @@ 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))) }) } - rm_func("rm") - rm_func("remove") + rmFunc("rm") + rmFunc("remove") It("podman network inspect no args", func() { session := podmanTest.Podman([]string{"network", "inspect"}) @@ -234,7 +233,7 @@ var _ = Describe("Podman network", func() { session := podmanTest.Podman(append([]string{"network", "inspect"}, expectedNetworks...)) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman network inspect", func() { @@ -269,7 +268,7 @@ var _ = Describe("Podman network", func() { Expect(ok).To(BeTrue()) Expect(net.NetworkID).To(Equal(netName)) Expect(net.IPPrefixLen).To(Equal(24)) - Expect(strings.HasPrefix(net.IPAddress, "10.50.50.")).To(BeTrue()) + Expect(net.IPAddress).To(HavePrefix("10.50.50.")) // Necessary to ensure the CNI network is removed cleanly rmAll := podmanTest.Podman([]string{"rm", "-t", "0", "-f", ctrName}) @@ -342,12 +341,12 @@ var _ = Describe("Podman network", func() { Expect(ok).To(BeTrue()) Expect(net1.NetworkID).To(Equal(netName1)) Expect(net1.IPPrefixLen).To(Equal(25)) - Expect(strings.HasPrefix(net1.IPAddress, "10.50.51.")).To(BeTrue()) + Expect(net1.IPAddress).To(HavePrefix("10.50.51.")) net2, ok := conData[0].NetworkSettings.Networks[netName2] Expect(ok).To(BeTrue()) Expect(net2.NetworkID).To(Equal(netName2)) Expect(net2.IPPrefixLen).To(Equal(26)) - Expect(strings.HasPrefix(net2.IPAddress, "10.50.51.")).To(BeTrue()) + Expect(net2.IPAddress).To(HavePrefix("10.50.51.")) // Necessary to ensure the CNI network is removed cleanly rmAll := podmanTest.Podman([]string{"rm", "-t", "0", "-f", ctrName}) diff --git a/test/e2e/play_build_test.go b/test/e2e/play_build_test.go index 564735e07..9bdf9d06b 100644 --- a/test/e2e/play_build_test.go +++ b/test/e2e/play_build_test.go @@ -95,6 +95,7 @@ LABEL marge=mom // Setup yamlDir := filepath.Join(tempdir, RandomString(12)) err := os.Mkdir(yamlDir, 0755) + Expect(err).To(BeNil(), "mkdir "+yamlDir) err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) Expect(err).To(BeNil()) app1Dir := filepath.Join(yamlDir, "foobar") @@ -131,6 +132,7 @@ LABEL marge=mom // Setup yamlDir := filepath.Join(tempdir, RandomString(12)) err := os.Mkdir(yamlDir, 0755) + Expect(err).To(BeNil(), "mkdir "+yamlDir) err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) Expect(err).To(BeNil()) app1Dir := filepath.Join(yamlDir, "foobar") @@ -167,6 +169,7 @@ LABEL marge=mom // Setup yamlDir := filepath.Join(tempdir, RandomString(12)) err := os.Mkdir(yamlDir, 0755) + Expect(err).To(BeNil(), "mkdir "+yamlDir) err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) Expect(err).To(BeNil()) @@ -213,6 +216,7 @@ LABEL marge=mom // Setup yamlDir := filepath.Join(tempdir, RandomString(12)) err := os.Mkdir(yamlDir, 0755) + Expect(err).To(BeNil(), "os.Mkdir "+yamlDir) err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) Expect(err).To(BeNil()) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 1a3b5f8df..fc939711f 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -4,8 +4,6 @@ import ( "bytes" "context" "fmt" - "github.com/containers/podman/v3/pkg/bindings" - "github.com/containers/podman/v3/pkg/bindings/play" "io/ioutil" "net" "net/url" @@ -17,6 +15,8 @@ import ( "time" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/bindings" + "github.com/containers/podman/v3/pkg/bindings/play" "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" @@ -319,16 +319,16 @@ spec: image: {{ .Image }} name: {{ .Name }} imagePullPolicy: {{ .PullPolicy }} - {{- if or .CpuRequest .CpuLimit .MemoryRequest .MemoryLimit }} + {{- if or .CPURequest .CPULimit .MemoryRequest .MemoryLimit }} resources: - {{- if or .CpuRequest .MemoryRequest }} + {{- if or .CPURequest .MemoryRequest }} requests: - {{if .CpuRequest }}cpu: {{ .CpuRequest }}{{ end }} + {{if .CPURequest }}cpu: {{ .CPURequest }}{{ end }} {{if .MemoryRequest }}memory: {{ .MemoryRequest }}{{ end }} {{- end }} - {{- if or .CpuLimit .MemoryLimit }} + {{- if or .CPULimit .MemoryLimit }} limits: - {{if .CpuLimit }}cpu: {{ .CpuLimit }}{{ end }} + {{if .CPULimit }}cpu: {{ .CPULimit }}{{ end }} {{if .MemoryLimit }}memory: {{ .MemoryLimit }}{{ end }} {{- end }} {{- end }} @@ -479,16 +479,16 @@ spec: image: {{ .Image }} name: {{ .Name }} imagePullPolicy: {{ .PullPolicy }} - {{- if or .CpuRequest .CpuLimit .MemoryRequest .MemoryLimit }} + {{- if or .CPURequest .CPULimit .MemoryRequest .MemoryLimit }} resources: - {{- if or .CpuRequest .MemoryRequest }} + {{- if or .CPURequest .MemoryRequest }} requests: - {{if .CpuRequest }}cpu: {{ .CpuRequest }}{{ end }} + {{if .CPURequest }}cpu: {{ .CPURequest }}{{ end }} {{if .MemoryRequest }}memory: {{ .MemoryRequest }}{{ end }} {{- end }} - {{- if or .CpuLimit .MemoryLimit }} + {{- if or .CPULimit .MemoryLimit }} limits: - {{if .CpuLimit }}cpu: {{ .CpuLimit }}{{ end }} + {{if .CPULimit }}cpu: {{ .CPULimit }}{{ end }} {{if .MemoryLimit }}memory: {{ .MemoryLimit }}{{ end }} {{- end }} {{- end }} @@ -820,12 +820,6 @@ func getDeployment(options ...deploymentOption) *Deployment { type deploymentOption func(*Deployment) -func withDeploymentLabel(k, v string) deploymentOption { - return func(deployment *Deployment) { - deployment.Labels[k] = v - } -} - func withDeploymentAnnotation(k, v string) deploymentOption { return func(deployment *Deployment) { deployment.Annotations[k] = v @@ -866,8 +860,8 @@ type Ctr struct { Image string Cmd []string Arg []string - CpuRequest string - CpuLimit string + CPURequest string + CPULimit string MemoryRequest string MemoryLimit string SecurityContext bool @@ -947,15 +941,15 @@ func withImage(img string) ctrOption { } } -func withCpuRequest(request string) ctrOption { +func withCPURequest(request string) ctrOption { return func(c *Ctr) { - c.CpuRequest = request + c.CPURequest = request } } -func withCpuLimit(limit string) ctrOption { +func withCPULimit(limit string) ctrOption { return func(c *Ctr) { - c.CpuLimit = limit + c.CPULimit = limit } } @@ -1848,7 +1842,7 @@ var _ = Describe("Podman play kube", func() { It("podman play kube seccomp container level", func() { SkipIfRemote("podman-remote does not support --seccomp-profile-root flag") // expect play kube is expected to set a seccomp label if it's applied as an annotation - jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM) + jsonFile, err := podmanTest.CreateSeccompJSON(seccompPwdEPERM) if err != nil { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") @@ -1861,7 +1855,7 @@ var _ = Describe("Podman play kube", func() { err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) - // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look + // CreateSeccompJSON will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -1875,7 +1869,7 @@ var _ = Describe("Podman play kube", func() { It("podman play kube seccomp pod level", func() { SkipIfRemote("podman-remote does not support --seccomp-profile-root flag") // expect play kube is expected to set a seccomp label if it's applied as an annotation - jsonFile, err := podmanTest.CreateSeccompJson(seccompPwdEPERM) + jsonFile, err := podmanTest.CreateSeccompJSON(seccompPwdEPERM) if err != nil { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") @@ -1888,7 +1882,7 @@ var _ = Describe("Podman play kube", func() { err = generateKubeYaml("pod", pod, kubeYaml) Expect(err).To(BeNil()) - // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look + // CreateSeccompJSON will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -2348,19 +2342,19 @@ VOLUME %s`, ALPINE, hostPathDir+"/") var ( numReplicas int32 = 3 - expectedCpuRequest string = "100m" - expectedCpuLimit string = "200m" + expectedCPURequest string = "100m" + expectedCPULimit string = "200m" expectedMemoryRequest string = "10000000" expectedMemoryLimit string = "20000000" ) - expectedCpuQuota := milliCPUToQuota(expectedCpuLimit) + expectedCPUQuota := milliCPUToQuota(expectedCPULimit) deployment := getDeployment( withReplicas(numReplicas), withPod(getPod(withCtr(getCtr( - withCpuRequest(expectedCpuRequest), - withCpuLimit(expectedCpuLimit), + withCPURequest(expectedCPURequest), + withCPULimit(expectedCPULimit), withMemoryRequest(expectedMemoryRequest), withMemoryLimit(expectedMemoryLimit), ))))) @@ -2372,6 +2366,7 @@ VOLUME %s`, ALPINE, hostPathDir+"/") Expect(kube).Should(Exit(0)) for _, pod := range getPodNamesInDeployment(deployment) { + pod := pod // copy into local scope inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", ` CpuPeriod: {{ .HostConfig.CpuPeriod }} CpuQuota: {{ .HostConfig.CpuQuota }} @@ -2379,7 +2374,7 @@ Memory: {{ .HostConfig.Memory }} MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s: %d", "CpuQuota", expectedCpuQuota))) + Expect(inspect.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s: %d", "CpuQuota", expectedCPUQuota))) Expect(inspect.OutputToString()).To(ContainSubstring("MemoryReservation: " + expectedMemoryRequest)) Expect(inspect.OutputToString()).To(ContainSubstring("Memory: " + expectedMemoryLimit)) } @@ -2391,12 +2386,12 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) podmanTest.CgroupManager = "systemd" var ( - expectedCpuLimit string = "1" + expectedCPULimit string = "1" ) deployment := getDeployment( withPod(getPod(withCtr(getCtr( - withCpuLimit(expectedCpuLimit), + withCPULimit(expectedCPULimit), ))))) err := generateKubeYaml("deployment", deployment, kubeYaml) Expect(err).To(BeNil()) @@ -2406,6 +2401,7 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) Expect(kube).Should(Exit(0)) for _, pod := range getPodNamesInDeployment(deployment) { + pod := pod // copy into local scope inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", `{{ .HostConfig.CpuPeriod }}:{{ .HostConfig.CpuQuota }}`}) inspect.WaitWithDefaultTimeout() @@ -3054,6 +3050,7 @@ ENV OPENJ9_JAVA_OPTIONS=%q deployment := getDeployment(withPod(pod)) deploymentYaml, err := getKubeYaml("deployment", deployment) + Expect(err).To(BeNil(), "getKubeYaml(deployment)") yamls := []string{cmYaml, deploymentYaml} err = generateMultiDocKubeYaml(yamls, kubeYaml) Expect(err).To(BeNil()) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index a3dde3650..186688b93 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -173,7 +173,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/hosts"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), "12.34.56.78 test.example.com")).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring("12.34.56.78 test.example.com")) }) It("podman create pod with --add-host and no infra should fail", func() { @@ -193,7 +193,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("nameserver %s", server))).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring("nameserver %s", server)) }) It("podman create pod with DNS server set and no infra should fail", func() { @@ -214,7 +214,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("options %s", option))).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring(fmt.Sprintf("options %s", option))) }) It("podman create pod with DNS option set and no infra should fail", func() { @@ -235,7 +235,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("search %s", search))).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring(fmt.Sprintf("search %s", search))) }) It("podman create pod with DNS search domain set and no infra should fail", func() { @@ -259,7 +259,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring(ip)) } }) @@ -302,7 +302,7 @@ var _ = Describe("Podman pod create", func() { podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) podResolvConf.WaitWithDefaultTimeout() Expect(podResolvConf).Should(Exit(0)) - Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue()) + Expect(podResolvConf.OutputToString()).To(ContainSubstring(mac)) } }) @@ -474,7 +474,7 @@ entrypoint ["/fromimage"] status1 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status1.WaitWithDefaultTimeout() Expect(status1).Should(Exit(0)) - Expect(strings.Contains(status1.OutputToString(), "Created")).To(BeTrue()) + Expect(status1.OutputToString()).To(ContainSubstring("Created")) ctr1 := podmanTest.Podman([]string{"run", "--pod", podName, "-d", ALPINE, "top"}) ctr1.WaitWithDefaultTimeout() @@ -483,7 +483,7 @@ entrypoint ["/fromimage"] status2 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status2.WaitWithDefaultTimeout() Expect(status2).Should(Exit(0)) - Expect(strings.Contains(status2.OutputToString(), "Running")).To(BeTrue()) + Expect(status2.OutputToString()).To(ContainSubstring("Running")) ctr2 := podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) ctr2.WaitWithDefaultTimeout() @@ -492,7 +492,7 @@ entrypoint ["/fromimage"] status3 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status3.WaitWithDefaultTimeout() Expect(status3).Should(Exit(0)) - Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) + Expect(status3.OutputToString()).To(ContainSubstring("Degraded")) }) It("podman create with unsupported network options", func() { @@ -725,7 +725,7 @@ ENTRYPOINT ["sleep","99999"] session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) l := session.OutputToString() - Expect(strings.Contains(l, "1024")).To(BeTrue()) + Expect(l).To(ContainSubstring("1024")) m[l] = l } // check for no duplicates diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index 5728cf9b9..8a6f2a367 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -56,7 +56,7 @@ var _ = Describe("Podman pod inspect", func() { inspect := podmanTest.Podman([]string{"pod", "inspect", podid}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) podData := inspect.InspectPodToJSON() Expect(podData.ID).To(Equal(podid)) }) @@ -75,7 +75,7 @@ var _ = Describe("Podman pod inspect", func() { inspect := podmanTest.Podman([]string{"pod", "inspect", podName}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) podData := inspect.InspectPodToJSON() // Let's get the last len(createCommand) items in the command. inspectCreateCommand := podData.CreateCommand diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 7dc3dfa7f..d9b0761fa 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -132,11 +132,11 @@ var _ = Describe("Podman pod rm", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) fmt.Printf("Started container running in one pod") - num_pods := podmanTest.NumberOfPods() - Expect(num_pods).To(Equal(2)) + numPods := podmanTest.NumberOfPods() + Expect(numPods).To(Equal(2)) ps := podmanTest.Podman([]string{"pod", "ps"}) ps.WaitWithDefaultTimeout() - fmt.Printf("Current %d pod(s):\n%s\n", num_pods, ps.OutputToString()) + fmt.Printf("Current %d pod(s):\n%s\n", numPods, ps.OutputToString()) fmt.Printf("Removing all empty pods\n") result := podmanTest.Podman([]string{"pod", "rm", "-a"}) @@ -145,11 +145,11 @@ var _ = Describe("Podman pod rm", func() { foundExpectedError, _ := result.ErrorGrepString("cannot be removed") Expect(foundExpectedError).To(Equal(true)) - num_pods = podmanTest.NumberOfPods() + numPods = podmanTest.NumberOfPods() ps = podmanTest.Podman([]string{"pod", "ps"}) ps.WaitWithDefaultTimeout() - fmt.Printf("Final %d pod(s):\n%s\n", num_pods, ps.OutputToString()) - Expect(num_pods).To(Equal(1)) + fmt.Printf("Final %d pod(s):\n%s\n", numPods, ps.OutputToString()) + Expect(numPods).To(Equal(1)) // Confirm top container still running inside remaining pod Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 5ec209034..bb145088e 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -149,7 +149,7 @@ var _ = Describe("Podman pod stats", func() { stats := podmanTest.Podman([]string{"pod", "stats", "--format", "json", "--no-stream", "-a"}) stats.WaitWithDefaultTimeout() Expect(stats).Should(Exit(0)) - Expect(stats.IsJSONOutputValid()).To(BeTrue()) + Expect(stats.OutputToString()).To(BeValidJSON()) }) It("podman pod stats with GO template", func() { _, ec, podid := podmanTest.CreatePod(nil) @@ -189,6 +189,6 @@ var _ = Describe("Podman pod stats", func() { stats := podmanTest.Podman([]string{"pod", "stats", "--format", "json", "--no-stream", podName}) stats.WaitWithDefaultTimeout() Expect(stats).Should(Exit(0)) - Expect(stats.IsJSONOutputValid()).To(BeTrue()) + Expect(stats.OutputToString()).To(BeValidJSON()) }) }) 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/ps_test.go b/test/e2e/ps_test.go index 666b70b09..3334250db 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -239,7 +239,7 @@ var _ = Describe("Podman ps", func() { result := podmanTest.Podman([]string{"ps", "--format", "json"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.IsJSONOutputValid()).To(BeTrue()) + Expect(result.OutputToString()).To(BeValidJSON()) }) It("podman ps namespace flag with json format", func() { @@ -249,7 +249,7 @@ var _ = Describe("Podman ps", func() { result := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "json"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.IsJSONOutputValid()).To(BeTrue()) + Expect(result.OutputToString()).To(BeValidJSON()) }) It("podman ps json format Created field is int64", func() { @@ -275,7 +275,7 @@ var _ = Describe("Podman ps", func() { result := podmanTest.Podman([]string{"ps", "-a", "--format", "json"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(result.IsJSONOutputValid()).To(BeTrue()) + Expect(result.OutputToString()).To(BeValidJSON()) // must contain "Status" match, StatusLine := result.GrepString(`Status`) Expect(match).To(BeTrue()) @@ -443,11 +443,10 @@ var _ = Describe("Podman ps", func() { // sanity check in case an oddly formatted size appears if len(matches1) < 2 || len(matches2) < 2 { return sortedArr[i] < sortedArr[j] - } else { - size1, _ := units.FromHumanSize(matches1[1]) - size2, _ := units.FromHumanSize(matches2[1]) - return size1 < size2 } + size1, _ := units.FromHumanSize(matches1[1]) + size2, _ := units.FromHumanSize(matches2[1]) + return size1 < size2 })).To(BeTrue()) }) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index fdb1b0c57..b2474b223 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -279,7 +279,7 @@ var _ = Describe("Podman pull", func() { // Pulling a multi-image archive without further specifying // which image _must_ error out. Pulling is restricted to one // image. - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) expectedError := "Unexpected tar manifest.json: expected 1 item, got 2" @@ -288,31 +288,31 @@ var _ = Describe("Podman pull", func() { // Now pull _one_ image from a multi-image archive via the name // and index syntax. - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@0")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:@0"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty:latest")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty:latest"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@1")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:@1"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty/but:different")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty/but:different"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) // Now check for some errors. - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:foo.com/does/not/exist:latest")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:foo.com/does/not/exist:latest"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) expectedError = "Tag \"foo.com/does/not/exist:latest\" not found" found, _ = session.ErrorGrepString(expectedError) Expect(found).To(Equal(true)) - session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@2")}) + session = podmanTest.Podman([]string{"pull", "docker-archive:./testdata/docker-two-images.tar.xz:@2"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(125)) expectedError = "Invalid source index @2, only 2 manifest items available" @@ -391,7 +391,7 @@ var _ = Describe("Podman pull", func() { setup := podmanTest.Podman([]string{"images", ALPINE, "-q", "--no-trunc"}) setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) - shortImageId := strings.Split(setup.OutputToString(), ":")[1] + shortImageID := strings.Split(setup.OutputToString(), ":")[1] rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() @@ -401,7 +401,7 @@ var _ = Describe("Podman pull", func() { pull.WaitWithDefaultTimeout() Expect(pull).Should(Exit(0)) - Expect(pull.OutputToString()).To(ContainSubstring(shortImageId)) + Expect(pull.OutputToString()).To(ContainSubstring(shortImageID)) }) It("podman pull check all tags", 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 196d8879d..2c2a9688e 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -97,19 +97,19 @@ var _ = Describe("Podman rmi", func() { setup := podmanTest.Podman([]string{"images", "-q", cirros}) setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) - cirrosId := setup.OutputToString() + cirrosID := setup.OutputToString() session := podmanTest.Podman([]string{"tag", "cirros", "foo:bar", "foo"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) // Trying without --force should fail - result := podmanTest.Podman([]string{"rmi", cirrosId}) + result := podmanTest.Podman([]string{"rmi", cirrosID}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) // With --force it should work - resultForce := podmanTest.Podman([]string{"rmi", "-f", cirrosId}) + resultForce := podmanTest.Podman([]string{"rmi", "-f", cirrosID}) resultForce.WaitWithDefaultTimeout() Expect(resultForce).Should(Exit(0)) }) @@ -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 e2004c8e0..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() { @@ -758,7 +757,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) run := podmanTest.Podman([]string{"run", "--net=host", "--hostname", hostname, ALPINE, "hostname"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) - Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) + Expect(run.OutputToString()).To(ContainSubstring(hostname)) }) It("podman run with --net=none sets hostname", func() { @@ -766,7 +765,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) run := podmanTest.Podman([]string{"run", "--net=none", "--hostname", hostname, ALPINE, "hostname"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) - Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) + Expect(run.OutputToString()).To(ContainSubstring(hostname)) }) It("podman run with --net=none adds hostname to /etc/hosts", func() { @@ -774,7 +773,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) run := podmanTest.Podman([]string{"run", "--net=none", "--hostname", hostname, ALPINE, "cat", "/etc/hosts"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) - Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) + Expect(run.OutputToString()).To(ContainSubstring(hostname)) }) It("podman run with pod does not add extra 127 entry to /etc/hosts", func() { @@ -789,7 +788,7 @@ EXPOSE 2004-2005/tcp`, ALPINE) Expect(run.OutputToString()).ToNot(ContainSubstring("127.0.0.1 %s", hostname)) }) - ping_test := func(netns string) { + pingTest := func(netns string) { hostname := "testctr" run := podmanTest.Podman([]string{"run", netns, "--hostname", hostname, ALPINE, "ping", "-c", "1", hostname}) run.WaitWithDefaultTimeout() @@ -801,11 +800,11 @@ EXPOSE 2004-2005/tcp`, ALPINE) } It("podman attempt to ping container name and hostname --net=none", func() { - ping_test("--net=none") + pingTest("--net=none") }) It("podman attempt to ping container name and hostname --net=private", func() { - ping_test("--net=private") + pingTest("--net=private") }) It("podman run check dnsname plugin", 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 d793a01f8..f1084a3d2 100644 --- a/test/e2e/run_privileged_test.go +++ b/test/e2e/run_privileged_test.go @@ -21,18 +21,18 @@ func containerCapMatchesHost(ctrCap string, hostCap string) { if isRootless() { return } - ctrCap_n, err := strconv.ParseUint(ctrCap, 16, 64) + ctrCapN, err := strconv.ParseUint(ctrCap, 16, 64) Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctrCap) - hostCap_n, err := strconv.ParseUint(hostCap, 16, 64) + hostCapN, err := strconv.ParseUint(hostCap, 16, 64) Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", hostCap) // host caps can never be zero (except rootless). // and host caps must always be a superset (inclusive) of container - Expect(hostCap_n).To(BeNumerically(">", 0), "host cap %q should be nonzero", hostCap) - Expect(hostCap_n).To(BeNumerically(">=", ctrCap_n), "host cap %q should never be less than container cap %q", hostCap, ctrCap) - hostCap_masked := hostCap_n & (1<<len(capability.List()) - 1) - Expect(ctrCap_n).To(Equal(hostCap_masked), "container cap %q is not a subset of host cap %q", ctrCap, hostCap) + Expect(hostCapN).To(BeNumerically(">", 0), "host cap %q should be nonzero", hostCap) + Expect(hostCapN).To(BeNumerically(">=", ctrCapN), "host cap %q should never be less than container cap %q", hostCap, ctrCap) + hostCapMasked := hostCapN & (1<<len(capability.List()) - 1) + Expect(ctrCapN).To(Equal(hostCapMasked), "container cap %q is not a subset of host cap %q", ctrCap, hostCap) } var _ = Describe("Podman privileged container tests", func() { @@ -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_selinux_test.go b/test/e2e/run_selinux_test.go index cf63760cc..a6672d45e 100644 --- a/test/e2e/run_selinux_test.go +++ b/test/e2e/run_selinux_test.go @@ -320,7 +320,7 @@ var _ = Describe("Podman run", func() { }) It("podman test init labels", func() { - session := podmanTest.Podman([]string{"create", ubi_init, "/sbin/init"}) + session := podmanTest.Podman([]string{"create", UBI_INIT, "/sbin/init"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) cid := session.OutputToString() 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 aa9037e56..3d4c1240e 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -318,7 +318,7 @@ var _ = Describe("Podman run", func() { forbidGetCWDSeccompProfile := func() string { in := []byte(`{"defaultAction":"SCMP_ACT_ALLOW","syscalls":[{"name":"getcwd","action":"SCMP_ACT_ERRNO"}]}`) - jsonFile, err := podmanTest.CreateSeccompJson(in) + jsonFile, err := podmanTest.CreateSeccompJSON(in) if err != nil { fmt.Println(err) Skip("Failed to prepare seccomp.json for test.") @@ -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() { @@ -1518,7 +1517,7 @@ USER mail`, BB) session := podmanTest.Podman([]string{"run", "-t", "-i", "--group-add", groupName, "--privileged", fedoraMinimal, "groups"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(strings.Contains(session.OutputToString(), groupName)).To(BeTrue()) + Expect(session.OutputToString()).To(ContainSubstring(groupName)) }) It("podman run --tz", func() { diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go index 9b981ef72..50f8087f1 100644 --- a/test/e2e/run_userns_test.go +++ b/test/e2e/run_userns_test.go @@ -153,7 +153,7 @@ var _ = Describe("Podman UserNS support", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) l := session.OutputToString() - Expect(strings.Contains(l, "1024")).To(BeTrue()) + Expect(l).To(ContainSubstring("1024")) m[l] = l } // check for no duplicates diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 967cf4a7c..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() @@ -151,6 +149,7 @@ var _ = Describe("Podman run with volumes", func() { Expect(err).To(BeNil()) testFile := filepath.Join(mountPath, "test1") f, err := os.Create(testFile) + Expect(err).To(BeNil(), "os.Create(testfile)") f.Close() Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/data", mountPath), redis, "ls", "/data/test1"}) @@ -194,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 @@ -286,7 +283,7 @@ var _ = Describe("Podman run with volumes", func() { os.Stderr.Sync() mountOut1 := strings.Join(strings.Fields(string(mountCmd1.Out.Contents())), " ") fmt.Printf("Output: %s", mountOut1) - Expect(strings.Contains(mountOut1, volName)).To(BeFalse()) + Expect(mountOut1).To(Not(ContainSubstring(volName))) ctrName := "testctr" podmanSession := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "-v", fmt.Sprintf("%s:/testvol", volName), ALPINE, "top"}) @@ -302,7 +299,7 @@ var _ = Describe("Podman run with volumes", func() { os.Stderr.Sync() mountOut2 := strings.Join(strings.Fields(string(mountCmd2.Out.Contents())), " ") fmt.Printf("Output: %s", mountOut2) - Expect(strings.Contains(mountOut2, volName)).To(BeTrue()) + Expect(mountOut2).To(ContainSubstring(volName)) // Stop the container to unmount podmanStopSession := podmanTest.Podman([]string{"stop", "--time", "0", ctrName}) @@ -323,7 +320,7 @@ var _ = Describe("Podman run with volumes", func() { os.Stderr.Sync() mountOut3 := strings.Join(strings.Fields(string(mountCmd3.Out.Contents())), " ") fmt.Printf("Output: %s", mountOut3) - Expect(strings.Contains(mountOut3, volName)).To(BeFalse()) + Expect(mountOut3).To(Not(ContainSubstring(volName))) }) It("podman named volume copyup", func() { @@ -515,7 +512,7 @@ RUN sh -c "cd /etc/apk && ln -s ../../testfile"`, ALPINE) Expect(runLs).Should(Exit(0)) outputArr := runLs.OutputToStringArray() Expect(len(outputArr)).To(Equal(1)) - Expect(strings.Contains(outputArr[0], fileName)).To(BeTrue()) + Expect(outputArr[0]).To(ContainSubstring(fileName)) }) It("Podman mount over image volume with trailing /", func() { @@ -550,6 +547,7 @@ VOLUME /test/`, ALPINE) os.Mkdir(mountPath, 0755) testFile := filepath.Join(mountPath, "test1") f, err := os.Create(testFile) + Expect(err).To(BeNil(), "os.Create "+testFile) f.Close() // Make sure host directory gets mounted in to container as overlay @@ -750,7 +748,7 @@ USER testuser`, fedoraMinimal) test1 := podmanTest.Podman([]string{"run", "-v", "testvol1:/test", imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) test1.WaitWithDefaultTimeout() Expect(test1).Should(Exit(0)) - Expect(strings.Contains(test1.OutputToString(), testString)).To(BeTrue()) + Expect(test1.OutputToString()).To(ContainSubstring(testString)) volName := "testvol2" vol := podmanTest.Podman([]string{"volume", "create", volName}) @@ -760,7 +758,7 @@ USER testuser`, fedoraMinimal) test2 := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/test", volName), imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) test2.WaitWithDefaultTimeout() Expect(test2).Should(Exit(0)) - Expect(strings.Contains(test2.OutputToString(), testString)).To(BeTrue()) + Expect(test2.OutputToString()).To(ContainSubstring(testString)) }) diff --git a/test/e2e/runlabel_test.go b/test/e2e/runlabel_test.go index 656eaaceb..b7b27dc14 100644 --- a/test/e2e/runlabel_test.go +++ b/test/e2e/runlabel_test.go @@ -94,18 +94,19 @@ var _ = Describe("podman container runlabel", func() { result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) // should not panic when label missing the value or don't have the label - Expect(result.LineInOutputContains("panic")).NotTo(BeTrue()) + Expect(result.OutputToString()).To(Not(ContainSubstring("panic"))) }) It("podman container runlabel bogus label in remote image should result in non-zero exit", func() { result := podmanTest.Podman([]string{"container", "runlabel", "RUN", "docker.io/library/ubuntu:latest"}) result.WaitWithDefaultTimeout() Expect(result).To(ExitWithError()) // should not panic when label missing the value or don't have the label - Expect(result.LineInOutputContains("panic")).NotTo(BeTrue()) + Expect(result.OutputToString()).To(Not(ContainSubstring("panic"))) }) It("podman container runlabel global options", func() { - Skip("Test nonfunctional for podman-in-podman testing") + fmt.Printf("FIXME: for lint. Remove when you fix this test: %s", GlobalDockerfile) + Skip("FIXME: $GLOBAL_OPTS does not work at all, #12436") image := "podman-global-test:ls" podmanTest.BuildImage(GlobalDockerfile, image, "false") result := podmanTest.Podman([]string{"--syslog", "--log-level", "debug", "container", "runlabel", "RUN", image}) diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go index 2ea88eb5e..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() { @@ -134,7 +130,7 @@ registries = ['{{.Host}}:{{.Port}}']` search := podmanTest.Podman([]string{"search", "--format", "json", "alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) - Expect(search.IsJSONOutputValid()).To(BeTrue()) + Expect(search.OutputToString()).To(BeValidJSON()) Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) // Test for https://github.com/containers/podman/issues/11894 @@ -151,7 +147,7 @@ registries = ['{{.Host}}:{{.Port}}']` search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", "alpine"}) search.WaitWithDefaultTimeout() Expect(search).Should(Exit(0)) - Expect(search.IsJSONOutputValid()).To(BeTrue()) + Expect(search.OutputToString()).To(BeValidJSON()) Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) Expect(search.OutputToString()).To(ContainSubstring("3.10")) Expect(search.OutputToString()).To(ContainSubstring("2.7")) @@ -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/secret_test.go b/test/e2e/secret_test.go index 8bbc889d7..758ed7edc 100644 --- a/test/e2e/secret_test.go +++ b/test/e2e/secret_test.go @@ -78,7 +78,7 @@ var _ = Describe("Podman secret", func() { inspect := podmanTest.Podman([]string{"secret", "inspect", secrID}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) }) It("podman secret inspect with --format", func() { @@ -115,7 +115,7 @@ var _ = Describe("Podman secret", func() { inspect := podmanTest.Podman([]string{"secret", "inspect", secrID, secrID2}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + Expect(inspect.OutputToString()).To(BeValidJSON()) }) It("podman secret inspect bogus", func() { @@ -208,7 +208,6 @@ var _ = Describe("Podman secret", func() { // no env variable set, should fail session := podmanTest.Podman([]string{"secret", "create", "--env", "a", "MYENVVAR"}) session.WaitWithDefaultTimeout() - secrID := session.OutputToString() Expect(session).To(ExitWithError()) os.Setenv("MYENVVAR", "somedata") @@ -218,7 +217,7 @@ var _ = Describe("Podman secret", func() { session = podmanTest.Podman([]string{"secret", "create", "--env", "a", "MYENVVAR"}) session.WaitWithDefaultTimeout() - secrID = session.OutputToString() + secrID := session.OutputToString() Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.ID}}", secrID}) diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go index c0d56fdbc..a58e2485c 100644 --- a/test/e2e/stats_test.go +++ b/test/e2e/stats_test.go @@ -148,7 +148,7 @@ var _ = Describe("Podman stats", func() { stats := podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "json"}) stats.WaitWithDefaultTimeout() Expect(stats).Should(Exit(0)) - Expect(stats.IsJSONOutputValid()).To(BeTrue()) + Expect(stats.OutputToString()).To(BeValidJSON()) }) It("podman stats on a container with no net ns", func() { diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 32c2cd1b8..bbbec1648 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -35,7 +35,7 @@ ExecStart=/usr/bin/podman start -a redis ExecStop=/usr/bin/podman stop -t 10 redis KillMode=process [Install] -WantedBy=multi-user.target +WantedBy=default.target ` }) @@ -50,8 +50,8 @@ WantedBy=multi-user.target SkipIfRootless("rootless can not write to /etc") SkipIfContainerized("test does not have systemd as pid 1") - sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemdUnitFile), 0644) - Expect(sys_file).To(BeNil()) + sysFile := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemdUnitFile), 0644) + Expect(sysFile).To(BeNil()) defer func() { stop := SystemExec("bash", []string{"-c", "systemctl stop redis"}) os.Remove("/etc/systemd/system/redis.service") @@ -78,7 +78,7 @@ WantedBy=multi-user.target It("podman run container with systemd PID1", func() { ctrName := "testSystemd" - run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", ubi_init, "/sbin/init"}) + run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", UBI_INIT, "/sbin/init"}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) @@ -93,7 +93,7 @@ WantedBy=multi-user.target systemctl := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "systemctl", "status", "--no-pager"}) systemctl.WaitWithDefaultTimeout() Expect(systemctl).Should(Exit(0)) - Expect(strings.Contains(systemctl.OutputToString(), "State:")).To(BeTrue()) + Expect(systemctl.OutputToString()).To(ContainSubstring("State:")) result := podmanTest.Podman([]string{"inspect", ctrName}) result.WaitWithDefaultTimeout() @@ -118,7 +118,7 @@ WantedBy=multi-user.target It("podman create container with systemd entrypoint triggers systemd mode", func() { ctrName := "testCtr" - run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", ubi_init}) + run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", UBI_INIT}) run.WaitWithDefaultTimeout() Expect(run).Should(Exit(0)) diff --git a/test/e2e/toolbox_test.go b/test/e2e/toolbox_test.go index e1fe44675..40db5180a 100644 --- a/test/e2e/toolbox_test.go +++ b/test/e2e/toolbox_test.go @@ -66,9 +66,7 @@ var _ = Describe("Toolbox-specific testing", func() { }) It("podman run --dns=none - allows self-management of /etc/resolv.conf", func() { - var session *PodmanSessionIntegration - - session = podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c", + session := podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c", "rm -f /etc/resolv.conf; touch -d '1970-01-01 00:02:03' /etc/resolv.conf; stat -c %s:%Y /etc/resolv.conf"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -76,9 +74,7 @@ var _ = Describe("Toolbox-specific testing", func() { }) It("podman run --no-hosts - allows self-management of /etc/hosts", func() { - var session *PodmanSessionIntegration - - session = podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c", + session := podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c", "rm -f /etc/hosts; touch -d '1970-01-01 00:02:03' /etc/hosts; stat -c %s:%Y /etc/hosts"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) @@ -164,9 +160,7 @@ var _ = Describe("Toolbox-specific testing", func() { }) It("podman create --userns=keep-id --user root:root - entrypoint - entrypoint is executed as root", func() { - var session *PodmanSessionIntegration - - session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE, + session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE, "id"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) 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/e2e/trust_test.go b/test/e2e/trust_test.go index b591e1c02..9a0d57d7a 100644 --- a/test/e2e/trust_test.go +++ b/test/e2e/trust_test.go @@ -76,7 +76,7 @@ var _ = Describe("Podman trust", func() { session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) var teststruct []map[string]string json.Unmarshal(session.Out.Contents(), &teststruct) Expect(len(teststruct)).To(Equal(3)) @@ -118,7 +118,7 @@ var _ = Describe("Podman trust", func() { Expect(session).Should(Exit(0)) contents, err := ioutil.ReadFile(filepath.Join(INTEGRATION_ROOT, "test/policy.json")) Expect(err).ShouldNot(HaveOccurred()) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) Expect(string(session.Out.Contents())).To(Equal(string(contents) + "\n")) }) }) diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go index d1f769724..90a9bfb0b 100644 --- a/test/e2e/volume_create_test.go +++ b/test/e2e/volume_create_test.go @@ -97,13 +97,12 @@ var _ = Describe("Podman volume create", func() { session = podmanTest.Podman([]string{"volume", "create", "my_vol2"}) session.WaitWithDefaultTimeout() - volName = session.OutputToString() Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "import", "my_vol2", "hello.tar"}) session.WaitWithDefaultTimeout() - volName = session.OutputToString() Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(""), "output of volume import") session = podmanTest.Podman([]string{"run", "--volume", "my_vol2:/data", ALPINE, "cat", "/data/test"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/volume_inspect_test.go b/test/e2e/volume_inspect_test.go index db95ba675..172063b90 100644 --- a/test/e2e/volume_inspect_test.go +++ b/test/e2e/volume_inspect_test.go @@ -2,7 +2,6 @@ package integration import ( "os" - "strings" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" @@ -43,7 +42,7 @@ var _ = Describe("Podman volume inspect", func() { session = podmanTest.Podman([]string{"volume", "inspect", volName}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman inspect volume with Go format", func() { @@ -86,6 +85,6 @@ var _ = Describe("Podman volume inspect", func() { inspect := podmanTest.Podman([]string{"volume", "inspect", volName}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(strings.Contains(inspect.OutputToString(), "tmpfs")).To(BeTrue()) + Expect(inspect.OutputToString()).To(ContainSubstring("tmpfs")) }) }) diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go index 6c4b22fa5..c1214366b 100644 --- a/test/e2e/volume_ls_test.go +++ b/test/e2e/volume_ls_test.go @@ -64,7 +64,7 @@ var _ = Describe("Podman volume ls", func() { session = podmanTest.Podman([]string{"volume", "ls", "--format", "json"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - Expect(session.IsJSONOutputValid()).To(BeTrue()) + Expect(session.OutputToString()).To(BeValidJSON()) }) It("podman ls volume with Go template", 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/python/docker/compat/test_containers.py b/test/python/docker/compat/test_containers.py index e6f7d992d..d14c09fc1 100644 --- a/test/python/docker/compat/test_containers.py +++ b/test/python/docker/compat/test_containers.py @@ -3,11 +3,13 @@ import subprocess import sys import time import unittest -from typing import IO, Optional +from typing import IO, Optional, List from docker import DockerClient, errors from docker.models.containers import Container from docker.models.images import Image +from docker.models.volumes import Volume +from docker.types import Mount from test.python.docker import Podman from test.python.docker.compat import common, constant @@ -207,9 +209,14 @@ class TestContainers(unittest.TestCase): def test_copy_to_container(self): ctr: Optional[Container] = None + vol: Optional[Volume] = None try: test_file_content = b"Hello World!" - ctr = self.client.containers.create(image="alpine", detach=True, command="top") + vol = self.client.volumes.create("test-volume") + ctr = self.client.containers.create(image="alpine", + detach=True, + command="top", + volumes=["test-volume:/test-volume-read-only:ro"]) ctr.start() buff: IO[bytes] = io.BytesIO() @@ -234,10 +241,16 @@ class TestContainers(unittest.TestCase): ret, out = ctr.exec_run(["cat", "/tmp/a.txt"]) self.assertEqual(ret, 0) self.assertEqual(out.rstrip(), test_file_content, "Content of copied file") + + buff.seek(0) + with self.assertRaises(errors.APIError): + ctr.put_archive("/test-volume-read-only/", buff) finally: if ctr is not None: ctr.stop() ctr.remove() + if vol is not None: + vol.remove(force=True) def test_mount_preexisting_dir(self): dockerfile = (B'FROM quay.io/libpod/alpine:latest\n' @@ -265,3 +278,25 @@ class TestContainers(unittest.TestCase): ctr.start() ret, out = ctr.exec_run(["stat", "/workspace/scratch/test"]) self.assertEqual(ret, 0, "Working directory created if it doesn't exist") + + def test_mount_rw_by_default(self): + ctr: Optional[Container] = None + vol: Optional[Volume] = None + try: + vol = self.client.volumes.create("test-volume") + ctr = self.client.containers.create(image="alpine", + detach=True, + command="top", + mounts=[Mount(target="/vol-mnt", + source="test-volume", + type='volume', + read_only=False)]) + ctr_inspect = self.client.api.inspect_container(ctr.id) + binds: List[str] = ctr_inspect["HostConfig"]["Binds"] + self.assertEqual(len(binds), 1) + self.assertEqual(binds[0], 'test-volume:/vol-mnt:rw,rprivate,nosuid,nodev,rbind') + finally: + if ctr is not None: + ctr.remove() + if vol is not None: + vol.remove(force=True) 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 +} diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index 4757f7643..56d36934d 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -34,6 +34,12 @@ function service_setup() { systemctl daemon-reload + # Also test enabling services (see #12438). + run systemctl enable "$SERVICE_NAME" + if [ $status -ne 0 ]; then + die "Error enabling systemd unit $SERVICE_NAME, output: $output" + fi + run systemctl start "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Error starting systemd unit $SERVICE_NAME, output: $output" @@ -53,6 +59,11 @@ function service_cleanup() { die "Error stopping systemd unit $SERVICE_NAME, output: $output" fi + run systemctl disable "$SERVICE_NAME" + if [ $status -ne 0 ]; then + die "Error disbling systemd unit $SERVICE_NAME, output: $output" + fi + if [[ -z "$status" ]]; then run systemctl is-active "$SERVICE_NAME" if [ $status -ne 0 ]; then diff --git a/test/system/255-auto-update.bats b/test/system/255-auto-update.bats index 99211f304..7540270bd 100644 --- a/test/system/255-auto-update.bats +++ b/test/system/255-auto-update.bats @@ -366,7 +366,7 @@ Type=oneshot ExecStart=/usr/bin/podman auto-update [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target EOF echo "podman-auto-update-$cname" >> $SNAME_FILE diff --git a/test/testvol/main.go b/test/testvol/main.go index a0f58348b..d15bf00cd 100644 --- a/test/testvol/main.go +++ b/test/testvol/main.go @@ -129,8 +129,8 @@ func (d *DirDriver) Capabilities() *volume.CapabilitiesResponse { logrus.Infof("Hit Capabilities() endpoint") return &volume.CapabilitiesResponse{ - volume.Capability{ - "local", + Capabilities: volume.Capability{ + Scope: "local", }, } } @@ -260,7 +260,7 @@ func (d *DirDriver) Path(req *volume.PathRequest) (*volume.PathResponse, error) } return &volume.PathResponse{ - vol.path, + Mountpoint: vol.path, }, nil } @@ -280,7 +280,7 @@ func (d *DirDriver) Mount(req *volume.MountRequest) (*volume.MountResponse, erro vol.mounts[req.ID] = true return &volume.MountResponse{ - vol.path, + Mountpoint: vol.path, }, nil } diff --git a/test/utils/common_function_test.go b/test/utils/common_function_test.go index 003d490ce..c996a302c 100644 --- a/test/utils/common_function_test.go +++ b/test/utils/common_function_test.go @@ -90,24 +90,24 @@ var _ = Describe("Common functions test", func() { Entry("Command exist", "Fakecmd", false), ) - It("Test WriteJsonFile", func() { - type testJson struct { + It("Test WriteJSONFile", func() { + type testJSON struct { Item1 int Item2 []string } - compareData := &testJson{} + compareData := &testJSON{} - testData := &testJson{ + testData := &testJSON{ Item1: 5, Item2: []string{"test"}, } testByte, _ := json.Marshal(testData) - err := WriteJsonFile(testByte, "/tmp/testJson") + err := WriteJSONFile(testByte, "/tmp/testJSON") Expect(err).To(BeNil(), "Failed to write JSON to file.") - read, err := os.Open("/tmp/testJson") + read, err := os.Open("/tmp/testJSON") defer read.Close() Expect(err).To(BeNil(), "Can not find the JSON file after we write it.") diff --git a/test/utils/matchers.go b/test/utils/matchers.go index 17ff3ea75..288779b63 100644 --- a/test/utils/matchers.go +++ b/test/utils/matchers.go @@ -1,11 +1,12 @@ package utils import ( + "encoding/json" "fmt" "net/url" "github.com/containers/common/pkg/config" - . "github.com/onsi/gomega" + . "github.com/onsi/gomega" //nolint:golint,stylecheck "github.com/onsi/gomega/format" "github.com/onsi/gomega/gexec" "github.com/onsi/gomega/matchers" @@ -95,7 +96,7 @@ func (matcher *URLMatcher) Match(actual interface{}) (bool, error) { if !ok { return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", matcher.Expected) } - e_uri, err := url.Parse(e) + eURI, err := url.Parse(e) if err != nil { return false, err } @@ -104,12 +105,12 @@ func (matcher *URLMatcher) Match(actual interface{}) (bool, error) { if !ok { return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", actual) } - a_uri, err := url.Parse(a) + aURI, err := url.Parse(a) if err != nil { return false, err } - return (&matchers.EqualMatcher{Expected: e_uri}).Match(a_uri) + return (&matchers.EqualMatcher{Expected: eURI}).Match(aURI) } type ExitMatcher struct { @@ -166,3 +167,32 @@ func (matcher *ExitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { } return true } + +type ValidJSONMatcher struct { + types.GomegaMatcher +} + +func BeValidJSON() *ValidJSONMatcher { + return &ValidJSONMatcher{} +} + +func (matcher *ValidJSONMatcher) Match(actual interface{}) (success bool, err error) { + s, ok := actual.(string) + if !ok { + return false, fmt.Errorf("ValidJSONMatcher expects a string, not %q", actual) + } + + var i interface{} + if err := json.Unmarshal([]byte(s), &i); err != nil { + return false, nil + } + return true, nil +} + +func (matcher *ValidJSONMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to be valid JSON") +} + +func (matcher *ValidJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to _not_ be valid JSON") +} diff --git a/test/utils/utils.go b/test/utils/utils.go index 4a57d9ce7..944c1ac3c 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -13,9 +13,9 @@ import ( "time" "github.com/containers/storage/pkg/parsers/kernel" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - . "github.com/onsi/gomega/gexec" + . "github.com/onsi/ginkgo" //nolint:golint,stylecheck + . "github.com/onsi/gomega" //nolint:golint,stylecheck + . "github.com/onsi/gomega/gexec" //nolint:golint,stylecheck ) var ( @@ -439,25 +439,21 @@ func IsKernelNewerThan(version string) (bool, error) { return true, nil } return false, nil - } // IsCommandAvailable check if command exist func IsCommandAvailable(command string) bool { check := exec.Command("bash", "-c", strings.Join([]string{"command -v", command}, " ")) err := check.Run() - if err != nil { - return false - } - return true + return err == nil } -// WriteJsonFile write json format data to a json file -func WriteJsonFile(data []byte, filePath string) error { +// WriteJSONFile write json format data to a json file +func WriteJSONFile(data []byte, filePath string) error { var jsonData map[string]interface{} json.Unmarshal(data, &jsonData) - formatJson, _ := json.MarshalIndent(jsonData, "", " ") - return ioutil.WriteFile(filePath, formatJson, 0644) + formatJSON, _ := json.MarshalIndent(jsonData, "", " ") + return ioutil.WriteFile(filePath, formatJSON, 0644) } // Containerized check the podman command run inside container @@ -471,10 +467,7 @@ func Containerized() bool { // shrug, if we cannot read that file, return false return false } - if strings.Index(string(b), "docker") > -1 { - return true - } - return false + return strings.Contains(string(b), "docker") } func init() { @@ -485,7 +478,6 @@ var randomLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ // RandomString returns a string of given length composed of random characters func RandomString(n int) string { - b := make([]rune, n) for i := range b { b[i] = randomLetters[rand.Intn(len(randomLetters))] diff --git a/troubleshooting.md b/troubleshooting.md index 44028fc42..2f5eb6552 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -767,7 +767,7 @@ Type=simple ExecStart=/bin/bash -c '/bin/busctl monitor --system --match "interface=org.fedoraproject.FirewallD1,member=Reloaded" --match "interface=org.fedoraproject.FirewallD1,member=PropertiesChanged" | while read -r line ; do podman network reload --all ; done' [Install] -WantedBy=multi-user.target +WantedBy=default.target ``` 2) For "systemctl restart firewalld", create a systemd unit file with the following ``` @@ -783,7 +783,7 @@ RemainAfterExit=yes ExecStart=/usr/bin/podman network reload --all [Install] -WantedBy=multi-user.target +WantedBy=default.target ``` However, If you use busctl monitor then you can't get machine-readable output on `RHEL 8`. Since it doesn't have `busctl -j` as mentioned here by [@yrro](https://github.com/containers/podman/issues/5431#issuecomment-896943018). @@ -802,7 +802,7 @@ ExecStart=/bin/bash -c "dbus-monitor --profile --system 'type=signal,sender=org. Restart=Always [Install] -WantedBy=multi-user.target +WantedBy=default.target ``` `busctl-monitor` is almost usable in `RHEL 8`, except that it always outputs two bogus events when it starts up, one of which is (in its only machine-readable format) indistinguishable from the `NameOwnerChanged` that you get when firewalld starts up. @@ -823,7 +823,7 @@ ExecStart=/usr/bin/python /path/to/python/code/podman-redo-nat.py Restart=always [Install] -WantedBy=multi-user.target +WantedBy=default.target ``` The code reloads podman network twice when you use `systemctl restart firewalld`. ``` diff --git a/vendor/github.com/containers/common/libimage/search.go b/vendor/github.com/containers/common/libimage/search.go index ece81531a..33a4776ce 100644 --- a/vendor/github.com/containers/common/libimage/search.go +++ b/vendor/github.com/containers/common/libimage/search.go @@ -58,6 +58,10 @@ type SearchOptions struct { InsecureSkipTLSVerify types.OptionalBool // ListTags returns the search result with available tags ListTags bool + // Registries to search if the specified term does not include a + // registry. If set, the unqualified-search registries in + // containers-registries.conf(5) are ignored. + Registries []string } // SearchFilter allows filtering images while searching. @@ -105,6 +109,10 @@ func ParseSearchFilter(filter []string) (*SearchFilter, error) { return sFilter, nil } +// Search searches term. If term includes a registry, only this registry will +// be used for searching. Otherwise, the unqualified-search registries in +// containers-registries.conf(5) or the ones specified in the options will be +// used. func (r *Runtime) Search(ctx context.Context, term string, options *SearchOptions) ([]SearchResult, error) { if options == nil { options = &SearchOptions{} @@ -117,10 +125,14 @@ func (r *Runtime) Search(ctx context.Context, term string, options *SearchOption // that we cannot use the reference parser from the containers/image // library as the search term may container arbitrary input such as // wildcards. See bugzilla.redhat.com/show_bug.cgi?id=1846629. - if spl := strings.SplitN(term, "/", 2); len(spl) > 1 { - searchRegistries = append(searchRegistries, spl[0]) + spl := strings.SplitN(term, "/", 2) + switch { + case len(spl) > 1: + searchRegistries = []string{spl[0]} term = spl[1] - } else { + case len(options.Registries) > 0: + searchRegistries = options.Registries + default: regs, err := sysregistriesv2.UnqualifiedSearchRegistries(r.systemContextCopy()) if err != nil { return nil, err diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index e1649ba8e..317f8922a 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -80,13 +80,13 @@ type copier struct { // imageCopier tracks state specific to a single image (possibly an item of a manifest list) type imageCopier struct { - c *copier - manifestUpdates *types.ManifestUpdateOptions - src types.Image - diffIDsAreNeeded bool - canModifyManifest bool - canSubstituteBlobs bool - ociEncryptLayers *[]int + c *copier + manifestUpdates *types.ManifestUpdateOptions + src types.Image + diffIDsAreNeeded bool + cannotModifyManifestReason string // The reason the manifest cannot be modified, or an empty string if it can + canSubstituteBlobs bool + ociEncryptLayers *[]int } const ( @@ -129,10 +129,14 @@ type Options struct { DestinationCtx *types.SystemContext ProgressInterval time.Duration // time to wait between reports to signal the progress channel Progress chan types.ProgressProperties // Reported to when ProgressInterval has arrived for a single artifact+offset. + + // Preserve digests, and fail if we cannot. + PreserveDigests bool // manifest MIME type of image set by user. "" is default and means use the autodetection to the the manifest MIME type ForceManifestMIMEType string ImageListSelection ImageListSelection // set to either CopySystemImage (the default), CopyAllImages, or CopySpecificImages to control which instances we copy when the source reference is a list; ignored if the source reference is not a list Instances []digest.Digest // if ImageListSelection is CopySpecificImages, copy only these instances and the list itself + // If OciEncryptConfig is non-nil, it indicates that an image should be encrypted. // The encryption options is derived from the construction of EncryptConfig object. // Note: During initial encryption process of a layer, the resultant digest is not known @@ -410,7 +414,36 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur return nil, errors.Wrapf(err, "Can not copy signatures to %s", transports.ImageName(c.dest.Reference())) } } - canModifyManifestList := (len(sigs) == 0) + + // If the destination is a digested reference, make a note of that, determine what digest value we're + // expecting, and check that the source manifest matches it. + destIsDigestedReference := false + if named := c.dest.Reference().DockerReference(); named != nil { + if digested, ok := named.(reference.Digested); ok { + destIsDigestedReference = true + matches, err := manifest.MatchesDigest(manifestList, digested.Digest()) + if err != nil { + return nil, errors.Wrapf(err, "computing digest of source image's manifest") + } + if !matches { + return nil, errors.New("Digest of source image's manifest would not match destination reference") + } + } + } + + // Determine if we're allowed to modify the manifest list. + // If we can, set to the empty string. If we can't, set to the reason why. + // Compare, and perhaps keep in sync with, the version in copyOneImage. + cannotModifyManifestListReason := "" + if len(sigs) > 0 { + cannotModifyManifestListReason = "Would invalidate signatures" + } + if destIsDigestedReference { + cannotModifyManifestListReason = "Destination specifies a digest" + } + if options.PreserveDigests { + cannotModifyManifestListReason = "Instructed to preserve digests" + } // Determine if we'll need to convert the manifest list to a different format. forceListMIMEType := options.ForceManifestMIMEType @@ -425,8 +458,8 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur return nil, errors.Wrapf(err, "determining manifest list type to write to destination") } if selectedListType != originalList.MIMEType() { - if !canModifyManifestList { - return nil, errors.Errorf("manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType) + if cannotModifyManifestListReason != "" { + return nil, errors.Errorf("Manifest list must be converted to type %q to be written to destination, but we cannot modify it: %q", selectedListType, cannotModifyManifestListReason) } } @@ -510,8 +543,8 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur // If we can't just use the original value, but we have to change it, flag an error. if !bytes.Equal(attemptedManifestList, originalManifestList) { - if !canModifyManifestList { - return nil, errors.Errorf(" manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", thisListType) + if cannotModifyManifestListReason != "" { + return nil, errors.Errorf("Manifest list must be converted to type %q to be written to destination, but we cannot modify it: %q", thisListType, cannotModifyManifestListReason) } logrus.Debugf("Manifest list has been updated") } else { @@ -629,13 +662,27 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli } } + // Determine if we're allowed to modify the manifest. + // If we can, set to the empty string. If we can't, set to the reason why. + // Compare, and perhaps keep in sync with, the version in copyMultipleImages. + cannotModifyManifestReason := "" + if len(sigs) > 0 { + cannotModifyManifestReason = "Would invalidate signatures" + } + if destIsDigestedReference { + cannotModifyManifestReason = "Destination specifies a digest" + } + if options.PreserveDigests { + cannotModifyManifestReason = "Instructed to preserve digests" + } + ic := imageCopier{ c: c, manifestUpdates: &types.ManifestUpdateOptions{InformationOnly: types.ManifestUpdateInformation{Destination: c.dest}}, src: src, // diffIDsAreNeeded is computed later - canModifyManifest: len(sigs) == 0 && !destIsDigestedReference, - ociEncryptLayers: options.OciEncryptLayers, + cannotModifyManifestReason: cannotModifyManifestReason, + ociEncryptLayers: options.OciEncryptLayers, } // Ensure _this_ copy sees exactly the intended data when either processing a signed image or signing it. // This may be too conservative, but for now, better safe than sorry, _especially_ on the SignBy path: @@ -643,7 +690,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli // We do intend the RecordDigestUncompressedPair calls to only work with reliable data, but at least there’s a risk // that the compressed version coming from a third party may be designed to attack some other decompressor implementation, // and we would reuse and sign it. - ic.canSubstituteBlobs = ic.canModifyManifest && options.SignBy == "" + ic.canSubstituteBlobs = ic.cannotModifyManifestReason == "" && options.SignBy == "" if err := ic.updateEmbeddedDockerReference(); err != nil { return nil, "", "", err @@ -710,10 +757,10 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli } // If the original MIME type is acceptable, determineManifestConversion always uses it as preferredManifestMIMEType. // So if we are here, we will definitely be trying to convert the manifest. - // With !ic.canModifyManifest, that would just be a string of repeated failures for the same reason, + // With ic.cannotModifyManifestReason != "", that would just be a string of repeated failures for the same reason, // so let’s bail out early and with a better error message. - if !ic.canModifyManifest { - return nil, "", "", errors.Wrap(err, "Writing manifest failed (and converting it is not possible, image is signed or the destination specifies a digest)") + if ic.cannotModifyManifestReason != "" { + return nil, "", "", errors.Wrapf(err, "Writing manifest failed and we cannot try conversions: %q", cannotModifyManifestReason) } // errs is a list of errors when trying various manifest types. Also serves as an "upload succeeded" flag when set to nil. @@ -813,9 +860,9 @@ func (ic *imageCopier) updateEmbeddedDockerReference() error { return nil // No reference embedded in the manifest, or it matches destRef already. } - if !ic.canModifyManifest { - return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would change the manifest, which is not possible (image is signed or the destination specifies a digest)", - transports.ImageName(ic.c.dest.Reference()), destRef.String()) + if ic.cannotModifyManifestReason != "" { + return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would change the manifest, which we cannot do: %q", + transports.ImageName(ic.c.dest.Reference()), destRef.String(), ic.cannotModifyManifestReason) } ic.manifestUpdates.EmbeddedDockerReference = destRef return nil @@ -833,7 +880,7 @@ func isTTY(w io.Writer) bool { return false } -// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest. +// copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.cannotModifyManifestReason == "". func (ic *imageCopier) copyLayers(ctx context.Context) error { srcInfos := ic.src.LayerInfos() numLayers := len(srcInfos) @@ -844,8 +891,8 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { srcInfosUpdated := false // If we only need to check authorization, no updates required. if updatedSrcInfos != nil && !reflect.DeepEqual(srcInfos, updatedSrcInfos) { - if !ic.canModifyManifest { - return errors.Errorf("Copying this image requires changing layer representation, which is not possible (image is signed or the destination specifies a digest)") + if ic.cannotModifyManifestReason != "" { + return errors.Errorf("Copying this image would require changing layer representation, which we cannot do: %q", ic.cannotModifyManifestReason) } srcInfos = updatedSrcInfos srcInfosUpdated = true @@ -975,8 +1022,8 @@ func layerDigestsDiffer(a, b []types.BlobInfo) bool { func (ic *imageCopier) copyUpdatedConfigAndManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, digest.Digest, error) { pendingImage := ic.src if !ic.noPendingManifestUpdates() { - if !ic.canModifyManifest { - return nil, "", errors.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden") + if ic.cannotModifyManifestReason != "" { + return nil, "", errors.Errorf("Internal error: copy needs an updated manifest but that was known to be forbidden: %q", ic.cannotModifyManifestReason) } if !ic.diffIDsAreNeeded && ic.src.UpdatedImageNeedsLayerDiffIDs(*ic.manifestUpdates) { // We have set ic.diffIDsAreNeeded based on the preferred MIME type returned by determineManifestConversion. @@ -1359,7 +1406,7 @@ func (ic *imageCopier) copyLayerFromStream(ctx context.Context, srcStream io.Rea } } - blobInfo, err := ic.c.copyBlobFromStream(ctx, srcStream, srcInfo, getDiffIDRecorder, ic.canModifyManifest, false, toEncrypt, bar, layerIndex, emptyLayer) // Sets err to nil on success + blobInfo, err := ic.c.copyBlobFromStream(ctx, srcStream, srcInfo, getDiffIDRecorder, ic.cannotModifyManifestReason == "", false, toEncrypt, bar, layerIndex, emptyLayer) // Sets err to nil on success return blobInfo, diffIDChan, err // We need the defer … pipeWriter.CloseWithError() to happen HERE so that the caller can block on reading from diffIDChan } diff --git a/vendor/github.com/containers/image/v5/copy/manifest.go b/vendor/github.com/containers/image/v5/copy/manifest.go index b97edbf08..86ec8863a 100644 --- a/vendor/github.com/containers/image/v5/copy/manifest.go +++ b/vendor/github.com/containers/image/v5/copy/manifest.go @@ -79,10 +79,10 @@ func (ic *imageCopier) determineManifestConversion(ctx context.Context, destSupp if _, ok := supportedByDest[srcType]; ok { prioritizedTypes.append(srcType) } - if !ic.canModifyManifest { - // We could also drop the !ic.canModifyManifest check and have the caller + if ic.cannotModifyManifestReason != "" { + // We could also drop this check and have the caller // make the choice; it is already doing that to an extent, to improve error - // messages. But it is nice to hide the “if !ic.canModifyManifest, do no conversion” + // messages. But it is nice to hide the “if we can't modify, do no conversion” // special case in here; the caller can then worry (or not) only about a good UI. logrus.Debugf("We can't modify the manifest, hoping for the best...") return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying? diff --git a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go index fb0a15b99..46c10ff63 100644 --- a/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go +++ b/vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go @@ -118,6 +118,7 @@ type Resolved struct { } func (r *Resolved) addCandidate(named reference.Named) { + named = reference.TagNameOnly(named) // Make sure to add ":latest" if needed r.PullCandidates = append(r.PullCandidates, PullCandidate{named, false, r}) } @@ -138,6 +139,8 @@ const ( rationaleUSR // Resolved value has been selected by the user (via the prompt). rationaleUserSelection + // Resolved value has been enforced to use Docker Hub (via SystemContext). + rationaleEnforcedDockerHub ) // Description returns a human-readable description about the resolution @@ -152,6 +155,8 @@ func (r *Resolved) Description() string { return fmt.Sprintf("Resolved %q as an alias (%s)", r.userInput, r.originDescription) case rationaleUSR: return fmt.Sprintf("Resolving %q using unqualified-search registries (%s)", r.userInput, r.originDescription) + case rationaleEnforcedDockerHub: + return fmt.Sprintf("Resolving %q to docker.io (%s)", r.userInput, r.originDescription) case rationaleUserSelection, rationaleNone: fallthrough default: @@ -265,8 +270,20 @@ func Resolve(ctx *types.SystemContext, name string) (*Resolved, error) { return nil, err } if !isShort { // no short name - named := reference.TagNameOnly(shortRef) // Make sure to add ":latest" if needed + resolved.addCandidate(shortRef) + return resolved, nil + } + + // Resolve to docker.io only if enforced by the caller (e.g., Podman's + // Docker-compatible REST API). + if ctx != nil && ctx.PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub { + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, errors.Wrapf(err, "cannot normalize input: %q", name) + } resolved.addCandidate(named) + resolved.rationale = rationaleEnforcedDockerHub + resolved.originDescription = "enforced by caller" return resolved, nil } @@ -295,9 +312,6 @@ func Resolve(ctx *types.SystemContext, name string) (*Resolved, error) { return nil, err } } - // Make sure to add ":latest" if needed - namedAlias = reference.TagNameOnly(namedAlias) - resolved.addCandidate(namedAlias) resolved.rationale = rationaleAlias resolved.originDescription = aliasOriginDescription @@ -325,9 +339,6 @@ func Resolve(ctx *types.SystemContext, name string) (*Resolved, error) { if err != nil { return nil, errors.Wrapf(err, "creating reference with unqualified-search registry %q", reg) } - // Make sure to add ":latest" if needed - named = reference.TagNameOnly(named) - resolved.addCandidate(named) } @@ -412,6 +423,23 @@ func ResolveLocally(ctx *types.SystemContext, name string) ([]reference.Named, e var candidates []reference.Named + // Complete the candidates with the specified registries. + completeCandidates := func(registries []string) ([]reference.Named, error) { + for _, reg := range registries { + named, err := reference.ParseNormalizedNamed(fmt.Sprintf("%s/%s", reg, name)) + if err != nil { + return nil, errors.Wrapf(err, "creating reference with unqualified-search registry %q", reg) + } + named = reference.TagNameOnly(named) // Make sure to add ":latest" if needed + candidates = append(candidates, named) + } + return candidates, nil + } + + if ctx != nil && ctx.PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub { + return completeCandidates([]string{"docker.io"}) + } + // Strip off the tag to normalize the short name for looking it up in // the config files. isTagged, isDigested, shortNameRepo, tag, digest := splitUserInput(shortRef) @@ -434,9 +462,7 @@ func ResolveLocally(ctx *types.SystemContext, name string) ([]reference.Named, e return nil, err } } - // Make sure to add ":latest" if needed - namedAlias = reference.TagNameOnly(namedAlias) - + namedAlias = reference.TagNameOnly(namedAlias) // Make sure to add ":latest" if needed candidates = append(candidates, namedAlias) } @@ -447,16 +473,5 @@ func ResolveLocally(ctx *types.SystemContext, name string) ([]reference.Named, e } // Note that "localhost" has precedence over the unqualified-search registries. - for _, reg := range append([]string{"localhost"}, unqualifiedSearchRegistries...) { - named, err := reference.ParseNormalizedNamed(fmt.Sprintf("%s/%s", reg, name)) - if err != nil { - return nil, errors.Wrapf(err, "creating reference with unqualified-search registry %q", reg) - } - // Make sure to add ":latest" if needed - named = reference.TagNameOnly(named) - - candidates = append(candidates, named) - } - - return candidates, nil + return completeCandidates(append([]string{"localhost"}, unqualifiedSearchRegistries...)) } diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go index c98a6c6fd..dcff8caf7 100644 --- a/vendor/github.com/containers/image/v5/types/types.go +++ b/vendor/github.com/containers/image/v5/types/types.go @@ -561,6 +561,11 @@ type SystemContext struct { UserShortNameAliasConfPath string // If set, short-name resolution in pkg/shortnames must follow the specified mode ShortNameMode *ShortNameMode + // If set, short names will resolve in pkg/shortnames to docker.io only, and unqualified-search registries and + // short-name aliases in registries.conf are ignored. Note that this field is only intended to help enforce + // resolving to Docker Hub in the Docker-compatible REST API of Podman; it should never be used outside this + // specific context. + PodmanOnlyShortNamesIgnoreRegistriesConfAndForceDockerHub bool // If not "", overrides the default path for the authentication file, but only new format files AuthFilePath string // if not "", overrides the default path for the authentication file, but with the legacy format; diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index ffb2a4ce2..17639f0d4 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -8,10 +8,10 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 17 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "" + VersionDev = "-dev" ) // Version is the specification version that the package types support. diff --git a/vendor/modules.txt b/vendor/modules.txt index 701c3f73a..a104465c6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -106,7 +106,7 @@ github.com/containers/buildah/pkg/rusage github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/util -# github.com/containers/common v0.46.1-0.20211122213330-d4e7724a0c58 +# github.com/containers/common v0.46.1-0.20211125160015-ccf46abecd91 ## explicit github.com/containers/common/libimage github.com/containers/common/libimage/manifests @@ -142,7 +142,7 @@ github.com/containers/common/version # github.com/containers/conmon v2.0.20+incompatible ## explicit github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.17.0 +# github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c ## explicit github.com/containers/image/v5/copy github.com/containers/image/v5/directory |