summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--OWNERS17
-rw-r--r--cmd/podman/common/completion.go6
-rw-r--r--cmd/podman/containers/mount.go14
-rw-r--r--cmd/podman/containers/ps.go24
-rw-r--r--cmd/podman/containers/stats.go33
-rw-r--r--cmd/podman/containers/top.go12
-rw-r--r--cmd/podman/images/history.go36
-rw-r--r--cmd/podman/images/list.go33
-rw-r--r--cmd/podman/images/mount.go15
-rw-r--r--cmd/podman/images/push.go4
-rw-r--r--cmd/podman/images/search.go35
-rw-r--r--cmd/podman/images/trust_show.go27
-rw-r--r--cmd/podman/inspect/inspect.go4
-rw-r--r--cmd/podman/machine/list.go14
-rw-r--r--cmd/podman/networks/list.go16
-rw-r--r--cmd/podman/pods/inspect.go2
-rw-r--r--cmd/podman/pods/ps.go3
-rw-r--r--cmd/podman/pods/stats.go8
-rw-r--r--cmd/podman/secrets/list.go5
-rw-r--r--cmd/podman/system/connection/list.go9
-rw-r--r--cmd/podman/system/version.go2
-rw-r--r--cmd/podman/volumes/list.go9
-rw-r--r--contrib/systemd/auto-update/podman-auto-update.service.in2
-rw-r--r--contrib/systemd/system/podman-restart.service.in2
-rw-r--r--contrib/systemd/system/podman.service.in2
-rw-r--r--docs/source/markdown/podman-generate-systemd.1.md6
-rw-r--r--docs/source/markdown/podman-push.1.md4
-rw-r--r--docs/source/markdown/podman-unshare.1.md5
-rw-r--r--go.mod4
-rw-r--r--go.sum9
-rw-r--r--libpod/container.go5
-rw-r--r--libpod/container_top_linux.go18
-rw-r--r--libpod/kube.go65
-rw-r--r--libpod/networking_linux.go26
-rw-r--r--pkg/api/handlers/compat/containers_create.go7
-rw-r--r--pkg/api/handlers/compat/images.go89
-rw-r--r--pkg/api/handlers/compat/images_build.go95
-rw-r--r--pkg/api/handlers/compat/images_history.go10
-rw-r--r--pkg/api/handlers/compat/images_push.go12
-rw-r--r--pkg/api/handlers/compat/images_remove.go8
-rw-r--r--pkg/api/handlers/compat/images_tag.go17
-rw-r--r--pkg/api/handlers/utils/images.go48
-rw-r--r--pkg/bindings/images/build.go59
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/infra/abi/generate.go12
-rw-r--r--pkg/domain/infra/abi/images.go9
-rw-r--r--pkg/domain/infra/abi/system.go7
-rw-r--r--pkg/domain/infra/tunnel/images.go5
-rw-r--r--pkg/machine/ignition.go4
-rw-r--r--pkg/specgen/generate/oci.go8
-rw-r--r--pkg/specgenutil/specgen.go17
-rw-r--r--pkg/systemd/generate/containers.go2
-rw-r--r--pkg/systemd/generate/containers_test.go40
-rw-r--r--pkg/systemd/generate/pods.go2
-rw-r--r--pkg/systemd/generate/pods_test.go10
-rw-r--r--test/apiv2/10-images.at7
-rw-r--r--test/apiv2/12-imagesMore.at3
-rw-r--r--test/apiv2/70-short-names.at148
-rw-r--r--test/apiv2/containers.conf8
-rw-r--r--test/e2e/build/Dockerfile.with-multiple-secret3
-rw-r--r--test/e2e/build/Dockerfile.with-secret2
-rw-r--r--test/e2e/build/Dockerfile.with-secret-verify-leak3
-rw-r--r--test/e2e/build/anothersecret.txt1
-rw-r--r--test/e2e/build/secret.txt1
-rw-r--r--test/e2e/build_test.go50
-rw-r--r--test/e2e/checkpoint_test.go12
-rw-r--r--test/e2e/commit_test.go8
-rw-r--r--test/e2e/common_test.go61
-rw-r--r--test/e2e/containers_conf_test.go2
-rw-r--r--test/e2e/create_test.go30
-rw-r--r--test/e2e/diff_test.go2
-rw-r--r--test/e2e/generate_kube_test.go17
-rw-r--r--test/e2e/generate_systemd_test.go2
-rw-r--r--test/e2e/history_test.go2
-rw-r--r--test/e2e/image_sign_test.go2
-rw-r--r--test/e2e/images_test.go13
-rw-r--r--test/e2e/inspect_test.go14
-rw-r--r--test/e2e/libpod_suite_remote_test.go21
-rw-r--r--test/e2e/libpod_suite_test.go12
-rw-r--r--test/e2e/load_test.go9
-rw-r--r--test/e2e/mount_test.go4
-rw-r--r--test/e2e/network_test.go13
-rw-r--r--test/e2e/pod_create_test.go20
-rw-r--r--test/e2e/pod_inspect_test.go4
-rw-r--r--test/e2e/pod_stats_test.go4
-rw-r--r--test/e2e/prune_test.go16
-rw-r--r--test/e2e/ps_test.go6
-rw-r--r--test/e2e/push_test.go32
-rw-r--r--test/e2e/restart_test.go4
-rw-r--r--test/e2e/rmi_test.go3
-rw-r--r--test/e2e/run_dns_test.go2
-rw-r--r--test/e2e/run_entrypoint_test.go2
-rw-r--r--test/e2e/run_networking_test.go12
-rw-r--r--test/e2e/run_passwd_test.go12
-rw-r--r--test/e2e/run_privileged_test.go4
-rw-r--r--test/e2e/run_signal_test.go6
-rw-r--r--test/e2e/run_test.go7
-rw-r--r--test/e2e/run_userns_test.go2
-rw-r--r--test/e2e/run_volume_test.go36
-rw-r--r--test/e2e/runlabel_test.go4
-rw-r--r--test/e2e/search_test.go27
-rw-r--r--test/e2e/secret_test.go4
-rw-r--r--test/e2e/stats_test.go2
-rw-r--r--test/e2e/systemd_test.go4
-rw-r--r--test/e2e/top_test.go5
-rw-r--r--test/e2e/trust_test.go4
-rw-r--r--test/e2e/volume_inspect_test.go5
-rw-r--r--test/e2e/volume_ls_test.go2
-rw-r--r--test/e2e/volume_rm_test.go2
-rw-r--r--test/system/010-images.bats2
-rw-r--r--test/system/110-history.bats8
-rw-r--r--test/system/180-blkio.bats69
-rw-r--r--test/system/250-systemd.bats11
-rw-r--r--test/system/255-auto-update.bats2
-rw-r--r--test/utils/matchers.go30
-rw-r--r--test/utils/utils.go1
-rw-r--r--troubleshooting.md8
-rw-r--r--vendor/github.com/containers/common/libimage/search.go18
-rw-r--r--vendor/github.com/containers/image/v5/copy/copy.go101
-rw-r--r--vendor/github.com/containers/image/v5/copy/manifest.go6
-rw-r--r--vendor/github.com/containers/image/v5/pkg/shortnames/shortnames.go59
-rw-r--r--vendor/github.com/containers/image/v5/types/types.go5
-rw-r--r--vendor/github.com/containers/image/v5/version/version.go4
-rw-r--r--vendor/modules.txt4
125 files changed, 1336 insertions, 618 deletions
diff --git a/Makefile b/Makefile
index ceecda274..3009f618d 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
diff --git a/OWNERS b/OWNERS
index 8f5368b00..e4c4faebb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,30 +1,35 @@
approvers:
+ - Luap99
+ - TomSweeneyRedHat
+ - ashley-cui
- baude
- edsantiago
+ - flouthoc
- giuseppe
- jwhonce
- - Luap99
+ - lsm5
- mheon
- mtrmac
- rhatdan
- saschagrunert
- - TomSweeneyRedHat
- umohnani8
- vrothberg
- zhangguanzhang
reviewers:
- - ashley-cui
+ - Luap99
+ - QiWang19
+ - TomSweeneyRedHat
- baude
+ - cdoern
- edsantiago
+ - flouthoc
- giuseppe
- jwhonce
- - Luap99
+ - lsm5
- mheon
- mtrmac
- - QiWang19
- rhatdan
- saschagrunert
- - TomSweeneyRedHat
- umohnani8
- vrothberg
- zhangguanzhang
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 4cb29383a..cb3efe592 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -1289,3 +1289,9 @@ func AutocompleteCheckpointCompressType(cmd *cobra.Command, args []string, toCom
types := []string{"gzip", "none", "zstd"}
return types, cobra.ShellCompDirectiveNoFileComp
}
+
+// AutocompleteCompressionFormat - Autocomplete compression-format type options.
+func AutocompleteCompressionFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ types := []string{"gzip", "zstd", "zstd:chunked"}
+ return types, cobra.ShellCompDirectiveNoFileComp
+}
diff --git a/cmd/podman/containers/mount.go b/cmd/podman/containers/mount.go
index 0397b456f..271fc4c6b 100644
--- a/cmd/podman/containers/mount.go
+++ b/cmd/podman/containers/mount.go
@@ -81,7 +81,7 @@ func init() {
validate.AddLatestFlag(containerMountCommand, &mountOpts.Latest)
}
-func mount(_ *cobra.Command, args []string) error {
+func mount(cmd *cobra.Command, args []string) error {
if len(args) > 0 && mountOpts.Latest {
return errors.Errorf("--latest and containers cannot be used together")
}
@@ -116,18 +116,14 @@ func mount(_ *cobra.Command, args []string) error {
mrs = append(mrs, mountReporter{r})
}
- format := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}"
- tmpl, err := report.NewTemplate("mounts").Parse(format)
- if err != nil {
- return err
- }
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
- w, err := report.NewWriterDefault(os.Stdout)
+ rpt, err = rpt.Parse(report.OriginPodman, "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}")
if err != nil {
return err
}
- defer w.Flush()
- return tmpl.Execute(w, mrs)
+ return rpt.Execute(mrs)
}
func printJSON(reports []*entities.ContainerMountReport) error {
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 712de327c..cebe61b5a 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -220,30 +220,28 @@ func ps(cmd *cobra.Command, _ []string) error {
hdrs, format := createPsOut()
+ var origin report.Origin
noHeading, _ := cmd.Flags().GetBool("noheading")
if cmd.Flags().Changed("format") {
noHeading = noHeading || !report.HasTable(listOpts.Format)
- format = report.NormalizeFormat(listOpts.Format)
- format = report.EnforceRange(format)
+ format = listOpts.Format
+ origin = report.OriginUser
+ } else {
+ origin = report.OriginPodman
}
ns := strings.NewReplacer(".Namespaces.", ".")
format = ns.Replace(format)
- tmpl, err := report.NewTemplate("list").Parse(format)
- if err != nil {
- return err
- }
-
- w, err := report.NewWriterDefault(os.Stdout)
+ rpt, err := report.New(os.Stdout, cmd.Name()).Parse(origin, format)
if err != nil {
return err
}
- defer w.Flush()
+ defer rpt.Flush()
headers := func() error { return nil }
if !noHeading {
headers = func() error {
- return tmpl.Execute(w, hdrs)
+ return rpt.Execute(hdrs)
}
}
@@ -268,10 +266,10 @@ func ps(cmd *cobra.Command, _ []string) error {
if err := headers(); err != nil {
return err
}
- if err := tmpl.Execute(w, responses); err != nil {
+ if err := rpt.Execute(responses); err != nil {
return err
}
- if err := w.Flush(); err != nil {
+ if err := rpt.Flush(); err != nil {
// we usually do not care about Flush() failures but here do not loop if Flush() has failed
return err
}
@@ -282,7 +280,7 @@ func ps(cmd *cobra.Command, _ []string) error {
if err := headers(); err != nil {
return err
}
- if err := tmpl.Execute(w, responses); err != nil {
+ if err := rpt.Execute(responses); err != nil {
return err
}
}
diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go
index d21feaabc..9fba51597 100644
--- a/cmd/podman/containers/stats.go
+++ b/cmd/podman/containers/stats.go
@@ -126,14 +126,14 @@ func stats(cmd *cobra.Command, args []string) error {
if report.Error != nil {
return report.Error
}
- if err := outputStats(report.Stats); err != nil {
+ if err := outputStats(cmd, report.Stats); err != nil {
return err
}
}
return nil
}
-func outputStats(reports []define.ContainerStats) error {
+func outputStats(cmd *cobra.Command, reports []define.ContainerStats) error {
headers := report.Headers(define.ContainerStats{}, map[string]string{
"ID": "ID",
"UpTime": "CPU TIME",
@@ -158,32 +158,27 @@ func outputStats(reports []define.ContainerStats) error {
if report.IsJSON(statsOptions.Format) {
return outputJSON(stats)
}
- format := "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\t{{.UpTime}}\t{{.AVGCPU}}\n"
- if len(statsOptions.Format) > 0 {
- format = report.NormalizeFormat(statsOptions.Format)
- }
- format = report.EnforceRange(format)
- tmpl, err := report.NewTemplate("stats").Parse(format)
- if err != nil {
- return err
- }
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
- w, err := report.NewWriterDefault(os.Stdout)
+ var err error
+ if cmd.Flags().Changed("format") {
+ rpt, err = rpt.Parse(report.OriginUser, statsOptions.Format)
+ } else {
+ format := "{{range .}}{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\t{{.UpTime}}\t{{.AVGCPU}}\n{{end -}}"
+ rpt, err = rpt.Parse(report.OriginPodman, format)
+ }
if err != nil {
return err
}
- defer w.Flush()
- if len(statsOptions.Format) < 1 {
- if err := tmpl.Execute(w, headers); err != nil {
+ if rpt.RenderHeaders {
+ if err := rpt.Execute(headers); err != nil {
return err
}
}
- if err := tmpl.Execute(w, stats); err != nil {
- return err
- }
- return nil
+ return rpt.Execute(stats)
}
type containerStats struct {
diff --git a/cmd/podman/containers/top.go b/cmd/podman/containers/top.go
index 808c6c494..963a251bb 100644
--- a/cmd/podman/containers/top.go
+++ b/cmd/podman/containers/top.go
@@ -77,7 +77,7 @@ func init() {
validate.AddLatestFlag(containerTopCommand, &topOptions.Latest)
}
-func top(_ *cobra.Command, args []string) error {
+func top(cmd *cobra.Command, args []string) error {
if topOptions.ListDescriptors {
descriptors, err := util.GetContainerPidInformationDescriptors()
if err != nil {
@@ -103,15 +103,13 @@ func top(_ *cobra.Command, args []string) error {
return err
}
- w, err := report.NewWriterDefault(os.Stdout)
- if err != nil {
- return err
- }
+ rpt := report.New(os.Stdout, cmd.Name()).Init(os.Stdout, 12, 2, 2, ' ', 0)
+ defer rpt.Flush()
for _, proc := range topResponse.Value {
- if _, err := fmt.Fprintln(w, proc); err != nil {
+ if _, err := fmt.Fprintln(rpt.Writer(), proc); err != nil {
return err
}
}
- return w.Flush()
+ return nil
}
diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go
index cc7b1b4eb..ac693a87b 100644
--- a/cmd/podman/images/history.go
+++ b/cmd/podman/images/history.go
@@ -1,7 +1,6 @@
package images
import (
- "context"
"fmt"
"os"
"strings"
@@ -79,7 +78,7 @@ func historyFlags(cmd *cobra.Command) {
}
func history(cmd *cobra.Command, args []string) error {
- results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{})
+ results, err := registry.ImageEngine().History(registry.Context(), args[0], entities.ImageHistoryOptions{})
if err != nil {
return err
}
@@ -111,37 +110,32 @@ func history(cmd *cobra.Command, args []string) error {
hr = append(hr, historyReporter{l})
}
- hdrs := report.Headers(historyReporter{}, map[string]string{
- "CreatedBy": "CREATED BY",
- })
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
- // Defaults
- row := "{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n"
switch {
- case cmd.Flags().Changed("format"):
- row = report.NormalizeFormat(opts.format)
case opts.quiet:
- row = "{{.ID}}\n"
+ rpt, err = rpt.Parse(report.OriginUser, "{{range .}}{{.ID}}\n{{end -}}")
+ case cmd.Flags().Changed("format"):
+ rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String())
+ default:
+ format := "{{range .}}{{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\n{{end -}}"
+ rpt, err = rpt.Parse(report.OriginPodman, format)
}
- format := report.EnforceRange(row)
-
- tmpl, err := report.NewTemplate("history").Parse(format)
if err != nil {
return err
}
- w, err := report.NewWriterDefault(os.Stdout)
- if err != nil {
- return err
- }
- defer w.Flush()
+ if rpt.RenderHeaders {
+ hdrs := report.Headers(historyReporter{}, map[string]string{
+ "CreatedBy": "CREATED BY",
+ })
- if !opts.quiet && !cmd.Flags().Changed("format") {
- if err := tmpl.Execute(w, hdrs); err != nil {
+ if err := rpt.Execute(hdrs); err != nil {
return errors.Wrapf(err, "failed to write report column headers")
}
}
- return tmpl.Execute(w, hr)
+ return rpt.Execute(hr)
}
type historyReporter struct {
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index 01286daf2..61514daa7 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -117,7 +117,7 @@ func images(cmd *cobra.Command, args []string) error {
listOptions.Filter = append(listOptions.Filter, "reference="+args[0])
}
- if cmd.Flag("sort").Changed && !sortFields.Contains(listFlag.sort) {
+ if cmd.Flags().Changed("sort") && !sortFields.Contains(listFlag.sort) {
return fmt.Errorf("\"%s\" is not a valid field for sorting. Choose from: %s",
listFlag.sort, sortFields.String())
}
@@ -140,7 +140,7 @@ func images(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) {
listFlag.noHeading = true
}
- return writeTemplate(imgs)
+ return writeTemplate(cmd, imgs)
}
}
@@ -186,38 +186,31 @@ func writeJSON(images []imageReporter) error {
return nil
}
-func writeTemplate(imgs []imageReporter) error {
+func writeTemplate(cmd *cobra.Command, imgs []imageReporter) error {
hdrs := report.Headers(imageReporter{}, map[string]string{
"ID": "IMAGE ID",
"ReadOnly": "R/O",
})
- var format string
- if listFlag.format == "" {
- format = lsFormatFromFlags(listFlag)
- } else {
- format = report.NormalizeFormat(listFlag.format)
- format = report.EnforceRange(format)
- }
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
- tmpl, err := report.NewTemplate("list").Parse(format)
- if err != nil {
- return err
+ var err error
+ if cmd.Flags().Changed("format") {
+ rpt, err = rpt.Parse(report.OriginUser, cmd.Flag("format").Value.String())
+ } else {
+ rpt, err = rpt.Parse(report.OriginPodman, lsFormatFromFlags(listFlag))
}
-
- w, err := report.NewWriterDefault(os.Stdout)
if err != nil {
return err
}
- defer w.Flush()
- if !listFlag.noHeading {
- if err := tmpl.Execute(w, hdrs); err != nil {
+ if rpt.RenderHeaders && !listFlag.noHeading {
+ if err := rpt.Execute(hdrs); err != nil {
return err
}
}
-
- return tmpl.Execute(w, imgs)
+ return rpt.Execute(imgs)
}
func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) {
diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go
index 89c00cb70..cdeb9eecf 100644
--- a/cmd/podman/images/mount.go
+++ b/cmd/podman/images/mount.go
@@ -87,7 +87,7 @@ func mount(cmd *cobra.Command, args []string) error {
case report.IsJSON(mountOpts.Format):
return printJSON(reports)
case mountOpts.Format == "":
- break // default format
+ break // see default format below
default:
return errors.Errorf("unknown --format argument: %q", mountOpts.Format)
}
@@ -97,19 +97,12 @@ func mount(cmd *cobra.Command, args []string) error {
mrs = append(mrs, mountReporter{r})
}
- row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}"
- tmpl, err := report.NewTemplate("mounts").Parse(row)
+ rpt, err := report.New(os.Stdout, cmd.Name()).Parse(report.OriginPodman, "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}")
if err != nil {
return err
}
-
- w, err := report.NewWriterDefault(os.Stdout)
- if err != nil {
- return err
- }
- defer w.Flush()
-
- return tmpl.Execute(w, mrs)
+ defer rpt.Flush()
+ return rpt.Execute(mrs)
}
func printJSON(reports []*entities.ImageMountReport) error {
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/cmd/podman/images/search.go b/cmd/podman/images/search.go
index c9a4793aa..0791ac02c 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -149,9 +149,9 @@ func imageSearch(cmd *cobra.Command, args []string) error {
searchReport[i].Description = d
}
- hdrs := report.Headers(entities.ImageSearchReport{}, nil)
- renderHeaders := true
- var row string
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
+
switch {
case searchOptions.ListTags:
if len(searchOptions.Filters) != 0 {
@@ -161,39 +161,30 @@ func imageSearch(cmd *cobra.Command, args []string) error {
listTagsEntries := buildListTagsJSON(searchReport)
return printArbitraryJSON(listTagsEntries)
}
- row = "{{.Name}}\t{{.Tag}}\n"
+ rpt, err = rpt.Parse(report.OriginPodman, "{{range .}}{{.Name}}\t{{.Tag}}\n{{end -}}")
case isJSON:
return printArbitraryJSON(searchReport)
case cmd.Flags().Changed("format"):
- renderHeaders = report.HasTable(searchOptions.Format)
- row = report.NormalizeFormat(searchOptions.Format)
+ rpt, err = rpt.Parse(report.OriginUser, searchOptions.Format)
default:
- row = "{{.Name}}\t{{.Description}}"
+ row := "{{.Name}}\t{{.Description}}"
if searchOptions.Compatible {
row += "\t{{.Stars}}\t{{.Official}}\t{{.Automated}}"
}
- row += "\n"
+ row = "{{range . }}" + row + "\n{{end -}}"
+ rpt, err = rpt.Parse(report.OriginPodman, row)
}
- format := report.EnforceRange(row)
-
- tmpl, err := report.NewTemplate("search").Parse(format)
if err != nil {
return err
}
- w, err := report.NewWriterDefault(os.Stdout)
- if err != nil {
- return err
- }
- defer w.Flush()
-
- if renderHeaders {
- if err := tmpl.Execute(w, hdrs); err != nil {
- return errors.Wrapf(err, "failed to write search column headers")
+ if rpt.RenderHeaders {
+ hdrs := report.Headers(entities.ImageSearchReport{}, nil)
+ if err := rpt.Execute(hdrs); err != nil {
+ return errors.Wrapf(err, "failed to write report column headers")
}
}
-
- return tmpl.Execute(w, searchReport)
+ return rpt.Execute(searchReport)
}
func printArbitraryJSON(v interface{}) error {
diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go
index c0e56f504..04ea24ca5 100644
--- a/cmd/podman/images/trust_show.go
+++ b/cmd/podman/images/trust_show.go
@@ -48,11 +48,12 @@ func showTrust(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- if showTrustOptions.Raw {
+
+ switch {
+ case showTrustOptions.Raw:
fmt.Println(string(trust.Raw))
return nil
- }
- if showTrustOptions.JSON {
+ case showTrustOptions.JSON:
b, err := json.MarshalIndent(trust.Policies, "", " ")
if err != nil {
return err
@@ -60,23 +61,13 @@ func showTrust(cmd *cobra.Command, args []string) error {
fmt.Println(string(b))
return nil
}
+ rpt := report.New(os.Stdout, cmd.Name())
+ defer rpt.Flush()
- format := "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}"
- tmpl, err := report.NewTemplate("list").Parse(format)
- if err != nil {
- return err
- }
-
- w, err := report.NewWriterDefault(os.Stdout)
+ rpt, err = rpt.Parse(report.OriginPodman,
+ "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}")
if err != nil {
return err
}
-
- if err := tmpl.Execute(w, trust.Policies); err != nil {
- return err
- }
- if err := w.Flush(); err != nil {
- return err
- }
- return nil
+ return rpt.Execute(trust.Policies)
}
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index 64b586388..c982b1b7f 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -215,8 +215,10 @@ func (i *inspector) inspect(namesOrIDs []string) error {
case report.IsJSON(i.options.Format) || i.options.Format == "":
err = printJSON(data)
default:
+ // Landing here implies user has given a custom --format
row := inspectNormalize(i.options.Format)
- row = "{{range . }}" + report.NormalizeFormat(row) + "{{end -}}"
+ row = report.NormalizeFormat(row)
+ row = report.EnforceRange(row)
err = printTmpl(tmpType, row, data)
}
if err != nil {
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index d569f4db0..774ab4fd0 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -116,7 +116,15 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error {
"DiskSize": "DISK SIZE",
})
- row := report.NormalizeFormat(listFlag.format)
+ var row string
+ switch {
+ case cmd.Flags().Changed("format"):
+ row = cmd.Flag("format").Value.String()
+ listFlag.noHeading = !report.HasTable(row)
+ row = report.NormalizeFormat(row)
+ default:
+ row = cmd.Flag("format").Value.String()
+ }
format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format)
@@ -130,10 +138,6 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error {
}
defer w.Flush()
- if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) {
- listFlag.noHeading = true
- }
-
if !listFlag.noHeading {
if err := tmpl.Execute(w, headers); err != nil {
return errors.Wrapf(err, "failed to write report column headers")
diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go
index 124a17d5d..6f1a7742a 100644
--- a/cmd/podman/networks/list.go
+++ b/cmd/podman/networks/list.go
@@ -84,7 +84,7 @@ func networkList(cmd *cobra.Command, args []string) error {
// table or other format output
default:
- err = templateOut(responses, cmd)
+ err = templateOut(cmd, responses)
}
return err
@@ -105,7 +105,7 @@ func jsonOut(responses []types.Network) error {
return nil
}
-func templateOut(responses []types.Network, cmd *cobra.Command) error {
+func templateOut(cmd *cobra.Command, responses []types.Network) error {
nlprs := make([]ListPrintReports, 0, len(responses))
for _, r := range responses {
nlprs = append(nlprs, ListPrintReports{r})
@@ -120,14 +120,16 @@ func templateOut(responses []types.Network, cmd *cobra.Command) error {
})
renderHeaders := report.HasTable(networkListOptions.Format)
- var row, format string
- if cmd.Flags().Changed("format") {
+ var row string
+ switch {
+ case cmd.Flags().Changed("format"):
row = report.NormalizeFormat(networkListOptions.Format)
- } else { // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" '
- renderHeaders = true
+ default:
+ // 'podman network ls' equivalent to 'podman network ls --format="table {{.ID}} {{.Name}} {{.Version}} {{.Plugins}}" '
row = "{{.ID}}\t{{.Name}}\t{{.Driver}}\n"
+ renderHeaders = true
}
- format = report.EnforceRange(row)
+ format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format)
if err != nil {
diff --git a/cmd/podman/pods/inspect.go b/cmd/podman/pods/inspect.go
index 96eaec3b9..d4b8df90e 100644
--- a/cmd/podman/pods/inspect.go
+++ b/cmd/podman/pods/inspect.go
@@ -64,11 +64,13 @@ func inspect(cmd *cobra.Command, args []string) error {
}
if report.IsJSON(inspectOptions.Format) {
+ json.MarshalIndent(responses, "", " ")
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(responses)
}
+ // cmd.Flags().Changed("format") must be true to reach this code
row := report.NormalizeFormat(inspectOptions.Format)
t, err := report.NewTemplate("inspect").Parse(row)
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 60aadf224..808ec31b3 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -136,11 +136,12 @@ func pods(cmd *cobra.Command, _ []string) error {
renderHeaders = report.HasTable(psInput.Format)
row = report.NormalizeFormat(psInput.Format)
}
+ format := report.EnforceRange(row)
+
noHeading, _ := cmd.Flags().GetBool("noheading")
if noHeading {
renderHeaders = false
}
- format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format)
if err != nil {
diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go
index ba2c1495b..a7bba3064 100644
--- a/cmd/podman/pods/stats.go
+++ b/cmd/podman/pods/stats.go
@@ -67,9 +67,7 @@ func stats(cmd *cobra.Command, args []string) error {
return err
}
- row := report.NormalizeFormat(statsOptions.Format)
- doJSON := report.IsJSON(row)
-
+ doJSON := report.IsJSON(cmd.Flag("format").Value.String())
headers := report.Headers(entities.PodStatsReport{}, map[string]string{
"CPU": "CPU %",
"MemUsage": "MEM USAGE/ LIMIT",
@@ -96,6 +94,8 @@ func stats(cmd *cobra.Command, args []string) error {
goterm.Flush()
}
if cmd.Flags().Changed("format") {
+ row := report.NormalizeFormat(statsOptions.Format)
+ row = report.EnforceRange(row)
if err := printFormattedPodStatsLines(headers, row, reports); err != nil {
return err
}
@@ -143,8 +143,6 @@ func printFormattedPodStatsLines(headerNames []map[string]string, row string, st
return nil
}
- row = report.EnforceRange(row)
-
tmpl, err := report.NewTemplate("stats").Parse(row)
if err != nil {
return err
diff --git a/cmd/podman/secrets/list.go b/cmd/podman/secrets/list.go
index f136de4ab..255d9ae1a 100644
--- a/cmd/podman/secrets/list.go
+++ b/cmd/podman/secrets/list.go
@@ -71,7 +71,10 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.SecretListReport)
"UpdatedAt": "UPDATED",
})
- row := report.NormalizeFormat(listFlag.format)
+ row := cmd.Flag("format").Value.String()
+ if cmd.Flags().Changed("format") {
+ row = report.NormalizeFormat(row)
+ }
format := report.EnforceRange(row)
tmpl, err := report.NewTemplate("list").Parse(format)
diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go
index f1f7657ad..2710142a8 100644
--- a/cmd/podman/system/connection/list.go
+++ b/cmd/podman/system/connection/list.go
@@ -82,7 +82,7 @@ func list(cmd *cobra.Command, _ []string) error {
return rows[i].Name < rows[j].Name
})
- format := "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n"
+ var format string
switch {
case report.IsJSON(cmd.Flag("format").Value.String()):
buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ")
@@ -90,11 +90,10 @@ func list(cmd *cobra.Command, _ []string) error {
fmt.Println(string(buf))
}
return err
+ case cmd.Flags().Changed("format"):
+ format = report.NormalizeFormat(cmd.Flag("format").Value.String())
default:
- if cmd.Flag("format").Changed {
- format = cmd.Flag("format").Value.String()
- format = report.NormalizeFormat(format)
- }
+ format = "{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\n"
}
format = report.EnforceRange(format)
diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go
index 3443978d6..87b806503 100644
--- a/cmd/podman/system/version.go
+++ b/cmd/podman/system/version.go
@@ -59,7 +59,7 @@ func version(cmd *cobra.Command, args []string) error {
}
defer w.Flush()
- if cmd.Flag("format").Changed {
+ if cmd.Flags().Changed("format") {
row := report.NormalizeFormat(versionFormat)
tmpl, err := report.NewTemplate("version 2.0.0").Parse(row)
if err != nil {
diff --git a/cmd/podman/volumes/list.go b/cmd/podman/volumes/list.go
index 0243054af..c372527de 100644
--- a/cmd/podman/volumes/list.go
+++ b/cmd/podman/volumes/list.go
@@ -97,9 +97,14 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.VolumeListReport)
"Name": "VOLUME NAME",
})
- row := report.NormalizeFormat(cliOpts.Format)
- if cliOpts.Quiet {
+ var row string
+ switch {
+ case cliOpts.Quiet:
row = "{{.Name}}\n"
+ case cmd.Flags().Changed("format"):
+ row = report.NormalizeFormat(cliOpts.Format)
+ default:
+ row = cmd.Flag("format").Value.String()
}
format := report.EnforceRange(row)
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-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-push.1.md b/docs/source/markdown/podman-push.1.md
index 55f294158..19c64a7e3 100644
--- a/docs/source/markdown/podman-push.1.md
+++ b/docs/source/markdown/podman-push.1.md
@@ -71,6 +71,10 @@ Please refer to containers-certs.d(5) for details. (This option is not available
Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type, compressed or uncompressed, as source)
Note: This flag can only be set when using the **dir** transport
+#### **--compression-format** *COMPRESSION*
+
+Specifies the compression format to use. Supported values are: `gzip`, `zstd` and `zstd:chunked`. The default is `gzip`.
+
#### **--digestfile** *Digestfile*
After copying the image, write the digest of the resulting image to the file. (This option is not available with the remote Podman client)
diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md
index 5676b1be7..01393a862 100644
--- a/docs/source/markdown/podman-unshare.1.md
+++ b/docs/source/markdown/podman-unshare.1.md
@@ -35,7 +35,6 @@ Print usage statement
Join the rootless network namespace used for CNI and netavark networking. It can be used to
connect to a rootless container via IP address (bridge networking). This is otherwise
not possible from the host network namespace.
-_Note: Using this option with more than one unshare session can have unexpected results._
## Exit Codes
@@ -57,13 +56,13 @@ the exit codes follow the `chroot` standard, see below:
**127** Executing a _contained command_ and the _command_ cannot be found
- $ podman run busybox foo; echo $?
+ $ podman unshare foo; echo $?
Error: fork/exec /usr/bin/bogus: no such file or directory
127
**Exit code** _contained command_ exit code
- $ podman run busybox /bin/sh -c 'exit 3'; echo $?
+ $ podman unshare /bin/sh -c 'exit 3'; echo $?
3
## EXAMPLE
diff --git a/go.mod b/go.mod
index 0e1ea3a7e..82dc3dfc3 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index 67eb41af1..09a30d2a7 100644
--- a/go.sum
+++ b/go.sum
@@ -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/libpod/kube.go b/libpod/kube.go
index 351c49c9a..4e61b5377 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -79,7 +79,11 @@ func (p *Pod) GenerateForKube(ctx context.Context) (*v1.Pod, []v1.ServicePort, e
if err != nil {
return nil, servicePorts, err
}
- servicePorts = containerPortsToServicePorts(ports)
+ spState := newServicePortState()
+ servicePorts, err = spState.containerPortsToServicePorts(ports)
+ if err != nil {
+ return nil, servicePorts, err
+ }
hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host))
}
pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork)
@@ -242,13 +246,17 @@ func ConvertV1PodToYAMLPod(pod *v1.Pod) *YAMLPod {
}
// GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object
-func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) YAMLService {
+func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) (YAMLService, error) {
service := YAMLService{}
selector := make(map[string]string)
selector["app"] = pod.Labels["app"]
ports := servicePorts
if len(ports) == 0 {
- ports = containersToServicePorts(pod.Spec.Containers)
+ p, err := containersToServicePorts(pod.Spec.Containers)
+ if err != nil {
+ return service, err
+ }
+ ports = p
}
serviceSpec := v1.ServiceSpec{
Ports: ports,
@@ -262,15 +270,43 @@ func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) YA
APIVersion: pod.TypeMeta.APIVersion,
}
service.TypeMeta = tm
- return service
+ return service, nil
+}
+
+// servicePortState allows calling containerPortsToServicePorts for a single service
+type servicePortState struct {
+ // A program using the shared math/rand state with the default seed will produce the same sequence of pseudo-random numbers
+ // for each execution. Use a private RNG state not to interfere with other users.
+ rng *rand.Rand
+ usedPorts map[int]struct{}
+}
+
+func newServicePortState() servicePortState {
+ return servicePortState{
+ rng: rand.New(rand.NewSource(time.Now().UnixNano())),
+ usedPorts: map[int]struct{}{},
+ }
}
// containerPortsToServicePorts takes a slice of containerports and generates a
// slice of service ports
-func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort {
+func (state *servicePortState) containerPortsToServicePorts(containerPorts []v1.ContainerPort) ([]v1.ServicePort, error) {
sps := make([]v1.ServicePort, 0, len(containerPorts))
for _, cp := range containerPorts {
- nodePort := 30000 + rand.Intn(32767-30000+1)
+ var nodePort int
+ attempt := 0
+ for {
+ // Legal nodeport range is 30000-32767
+ nodePort = 30000 + state.rng.Intn(32767-30000+1)
+ if _, found := state.usedPorts[nodePort]; !found {
+ state.usedPorts[nodePort] = struct{}{}
+ break
+ }
+ attempt++
+ if attempt >= 100 {
+ return nil, fmt.Errorf("too many attempts trying to generate a unique NodePort number")
+ }
+ }
servicePort := v1.ServicePort{
Protocol: cp.Protocol,
Port: cp.ContainerPort,
@@ -280,21 +316,22 @@ func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.Servic
}
sps = append(sps, servicePort)
}
- return sps
+ return sps, nil
}
// containersToServicePorts takes a slice of v1.Containers and generates an
// inclusive list of serviceports to expose
-func containersToServicePorts(containers []v1.Container) []v1.ServicePort {
- // Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers
- // for each execution. Legal nodeport range is 30000-32767
- rand.Seed(time.Now().UnixNano())
-
+func containersToServicePorts(containers []v1.Container) ([]v1.ServicePort, error) {
+ state := newServicePortState()
sps := make([]v1.ServicePort, 0, len(containers))
for _, ctr := range containers {
- sps = append(sps, containerPortsToServicePorts(ctr.Ports)...)
+ ports, err := state.containerPortsToServicePorts(ctr.Ports)
+ if err != nil {
+ return nil, err
+ }
+ sps = append(sps, ports...)
}
- return sps
+ return sps, nil
}
func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) {
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 7d1214183..b734b9c95 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -322,17 +322,14 @@ func (r *RootlessNetNS) Do(toRun func() error) error {
// Cleanup the rootless network namespace if needed.
// It checks if we have running containers with the bridge network mode.
-// Cleanup() will try to lock RootlessNetNS, therefore you have to call
-// it with an unlocked lock.
+// Cleanup() expects that r.Lock is locked
func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
_, err := os.Stat(r.dir)
if os.IsNotExist(err) {
// the directory does not exists no need for cleanup
return nil
}
- r.Lock.Lock()
- defer r.Lock.Unlock()
- running := func(c *Container) bool {
+ activeNetns := func(c *Container) bool {
// no bridge => no need to check
if !c.config.NetMode.IsBridge() {
return false
@@ -352,15 +349,18 @@ func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
return false
}
- state := c.state.State
- return state == define.ContainerStateRunning
+ // only check for an active netns, we cannot use the container state
+ // because not running does not mean that the netns does not need cleanup
+ // only if the netns is empty we know that we do not need cleanup
+ return c.state.NetNS != nil
}
- ctrs, err := runtime.GetContainersWithoutLock(running)
+ ctrs, err := runtime.GetContainersWithoutLock(activeNetns)
if err != nil {
return err
}
- // no cleanup if we found containers
- if len(ctrs) > 0 {
+ // no cleanup if we found no other containers with a netns
+ // we will always find one container (the container cleanup that is currently calling us)
+ if len(ctrs) > 1 {
return nil
}
logrus.Debug("Cleaning up rootless network namespace")
@@ -809,10 +809,10 @@ func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error {
if rootlessNetNS != nil {
// execute the cni setup in the rootless net ns
err = rootlessNetNS.Do(tearDownPod)
- rootlessNetNS.Lock.Unlock()
- if err == nil {
- err = rootlessNetNS.Cleanup(r)
+ if cerr := rootlessNetNS.Cleanup(r); cerr != nil {
+ logrus.WithError(err).Error("failed to cleanup rootless netns")
}
+ rootlessNetNS.Lock.Unlock()
} else {
err = tearDownPod()
}
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..4533fddeb 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.
@@ -393,14 +409,20 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
return
}
- var summaries = make([]*entities.ImageSummary, len(images))
- for j, img := range images {
+
+ summaries := make([]*entities.ImageSummary, 0, len(images))
+ for _, img := range images {
+ // If the image is a manifest list, extract as much as we can.
+ if isML, _ := img.IsManifestList(r.Context()); isML {
+ continue
+ }
+
is, err := handlers.ImageToImageSummary(img)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries"))
return
}
- summaries[j] = is
+ summaries = append(summaries, is)
}
utils.WriteResponse(w, http.StatusOK, summaries)
}
@@ -483,7 +505,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 ac5934c13..f85df02e1 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -118,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",
@@ -144,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 {
@@ -239,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
@@ -257,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{}
@@ -404,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()
@@ -447,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,
@@ -458,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,
@@ -481,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 {
@@ -590,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 3b0bebe9f..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"
@@ -345,6 +346,11 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
c = tmpFile.Name()
}
+ cfDir := filepath.Dir(c)
+ if absDir, err := filepath.EvalSymlinks(cfDir); err == nil {
+ name := filepath.ToSlash(strings.TrimPrefix(c, cfDir+string(filepath.Separator)))
+ c = filepath.Join(absDir, name)
+ }
containerfile, err := filepath.Abs(c)
if err != nil {
logrus.Errorf("Cannot find absolute path of %v: %v", c, err)
@@ -377,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/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/generate.go b/pkg/domain/infra/abi/generate.go
index 0defa1923..68bb351bf 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -139,7 +139,11 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
podContent = append(podContent, b)
if options.Service {
- b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}))
+ svc, err := libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{})
+ if err != nil {
+ return nil, err
+ }
+ b, err := generateKubeYAML(svc)
if err != nil {
return nil, err
}
@@ -177,7 +181,11 @@ func getKubePods(ctx context.Context, pods []*libpod.Pod, getService bool) ([][]
pos = append(pos, b)
if getService {
- b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, sp))
+ svc, err := libpod.GenerateKubeServiceFromV1Pod(po, sp)
+ if err != nil {
+ return nil, nil, err
+ }
+ b, err := generateKubeYAML(svc)
if err != nil {
return nil, nil, err
}
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/abi/system.go b/pkg/domain/infra/abi/system.go
index 7da7754f2..e6c9d850b 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -365,9 +365,12 @@ func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options e
if err != nil {
return err
}
- // make sure to unlock, unshare can run for a long time
+ // Make sure to unlock, unshare can run for a long time.
rootlessNetNS.Lock.Unlock()
- defer rootlessNetNS.Cleanup(ic.Libpod)
+ // We do not want to cleanup the netns after unshare.
+ // The problem is that we cannot know if we need to cleanup and
+ // secondly unshare should allow user to setup the namespace with
+ // special things, e.g. potentially macvlan or something like that.
return rootlessNetNS.Do(unshare)
}
return unshare()
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/10-images.at b/test/apiv2/10-images.at
index b7bcaf81d..e67f559f3 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -10,6 +10,13 @@ t GET libpod/images/json 200 \
.[0].Id~[0-9a-f]\\{64\\}
iid=$(jq -r '.[0].Id' <<<"$output")
+# Create an empty manifest and make sure it is not listed
+# in the compat endpoint.
+t GET images/json 200 length=1
+podman manifest create foo
+t GET images/json 200 length=1
+t GET libpod/images/json 200 length=2
+
t GET libpod/images/$iid/exists 204
t GET libpod/images/$PODMAN_TEST_IMAGE_NAME/exists 204
t GET libpod/images/${iid}abcdef/exists 404 \
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 318e3617e..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()
@@ -1297,8 +1297,8 @@ var _ = Describe("Podman checkpoint", func() {
})
ps.WaitWithDefaultTimeout()
Expect(ps).Should(Exit(0))
- Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse())
- Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse())
+ Expect(ps.OutputToString()).To(Not(ContainSubstring(session1.OutputToString())))
+ Expect(ps.OutputToString()).To(Not(ContainSubstring(session2.OutputToString())))
result = podmanTest.Podman([]string{
"container",
diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go
index 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 63cb4f091..a411a860b 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -36,7 +36,6 @@ var (
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() //nolint:golint,stylecheck
@@ -46,7 +45,7 @@ var (
type PodmanTestIntegration struct {
PodmanTest
ConmonBinary string
- CrioRoot string
+ Root string
CNIConfigDir string
OCIRuntime string
RunRoot string
@@ -111,13 +110,6 @@ var _ = SynchronizedBeforeSuite(func() []byte {
cwd, _ := os.Getwd()
INTEGRATION_ROOT = filepath.Join(cwd, "../../")
podman := PodmanTestSetup("/tmp")
- podman.ArtifactPath = ARTIFACT_DIR
- if _, err := os.Stat(ARTIFACT_DIR); os.IsNotExist(err) {
- if err = os.Mkdir(ARTIFACT_DIR, 0777); err != nil {
- fmt.Printf("%q\n", err)
- os.Exit(1)
- }
- }
// Pull cirros but don't put it into the cache
pullImages := []string{cirros, fedoraToolbox, volumeTest}
@@ -130,7 +122,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
fmt.Printf("%q\n", err)
os.Exit(1)
}
- podman.CrioRoot = ImageCacheDir
+ podman.Root = ImageCacheDir
// If running localized tests, the cache dir is created and populated. if the
// tests are remote, this is a no-op
populateCache(podman)
@@ -170,7 +162,6 @@ var _ = SynchronizedBeforeSuite(func() []byte {
func (p *PodmanTestIntegration) Setup() {
cwd, _ := os.Getwd()
INTEGRATION_ROOT = filepath.Join(cwd, "../../")
- p.ArtifactPath = ARTIFACT_DIR
}
var _ = SynchronizedAfterSuite(func() {},
@@ -181,14 +172,14 @@ var _ = SynchronizedAfterSuite(func() {},
fmt.Printf("%s\t\t%f\n", result.name, result.length)
}
- // previous crio-run
+ // previous runroot
tempdir, err := CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest := PodmanTestCreate(tempdir)
- if err := os.RemoveAll(podmanTest.CrioRoot); err != nil {
+ if err := os.RemoveAll(podmanTest.Root); err != nil {
fmt.Printf("%q\n", err)
}
@@ -265,18 +256,17 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
PodmanTest: PodmanTest{
PodmanBinary: podmanBinary,
RemotePodmanBinary: podmanRemoteBinary,
- ArtifactPath: ARTIFACT_DIR,
TempDir: tempDir,
RemoteTest: remote,
ImageCacheFS: storageFs,
ImageCacheDir: ImageCacheDir,
},
ConmonBinary: conmonBinary,
- CrioRoot: filepath.Join(tempDir, "crio"),
+ Root: filepath.Join(tempDir, "root"),
TmpDir: tempDir,
CNIConfigDir: CNIConfigDir,
OCIRuntime: ociRuntime,
- RunRoot: filepath.Join(tempDir, "crio-run"),
+ RunRoot: filepath.Join(tempDir, "runroot"),
StorageOptions: storageOptions,
SignaturePolicyPath: filepath.Join(INTEGRATION_ROOT, "test/policy.json"),
CgroupManager: cgroupManager,
@@ -308,15 +298,29 @@ func (p PodmanTestIntegration) AddImageToRWStore(image string) {
}
}
-// createArtifact creates a cached image in the artifact dir
+func imageTarPath(image string) string {
+ cacheDir := os.Getenv("PODMAN_TEST_IMAGE_CACHE_DIR")
+ if cacheDir == "" {
+ cacheDir = os.Getenv("TMPDIR")
+ if cacheDir == "" {
+ cacheDir = "/tmp"
+ }
+ }
+
+ // e.g., registry.com/fubar:latest -> registry.com-fubar-latest.tar
+ imageCacheName := strings.Replace(strings.Replace(image, ":", "-", -1), "/", "-", -1) + ".tar"
+
+ return filepath.Join(cacheDir, imageCacheName)
+}
+
+// createArtifact creates a cached image tarball in a local directory
func (p *PodmanTestIntegration) createArtifact(image string) {
if os.Getenv("NO_TEST_CACHE") != "" {
return
}
- dest := strings.Split(image, "/")
- destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
- fmt.Printf("Caching %s at %s...\n", image, destName)
+ destName := imageTarPath(image)
if _, err := os.Stat(destName); os.IsNotExist(err) {
+ fmt.Printf("Caching %s at %s...\n", image, destName)
pull := p.PodmanNoCache([]string{"pull", image})
pull.Wait(440)
Expect(pull).Should(Exit(0))
@@ -326,7 +330,7 @@ func (p *PodmanTestIntegration) createArtifact(image string) {
Expect(save).Should(Exit(0))
fmt.Printf("\n")
} else {
- fmt.Printf(" already exists.\n")
+ fmt.Printf("[image already cached: %s]\n", destName)
}
}
@@ -738,12 +742,13 @@ func (p *PodmanTestIntegration) RestartRemoteService() {
// RestoreArtifactToCache populates the imagecache from tarballs that were cached earlier
func (p *PodmanTestIntegration) RestoreArtifactToCache(image string) error {
- fmt.Printf("Restoring %s...\n", image)
- dest := strings.Split(image, "/")
- destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
- p.CrioRoot = p.ImageCacheDir
- restore := p.PodmanNoEvents([]string{"load", "-q", "-i", destName})
- restore.WaitWithDefaultTimeout()
+ tarball := imageTarPath(image)
+ if _, err := os.Stat(tarball); err == nil {
+ fmt.Printf("Restoring %s...\n", image)
+ p.Root = p.ImageCacheDir
+ restore := p.PodmanNoEvents([]string{"load", "-q", "-i", tarball})
+ restore.WaitWithDefaultTimeout()
+ }
return nil
}
@@ -795,7 +800,7 @@ func (p *PodmanTestIntegration) makeOptions(args []string, noEvents, noCache boo
}
podmanOptions := strings.Split(fmt.Sprintf("%s--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s --tmpdir %s --events-backend %s",
- debug, p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager, p.TmpDir, eventsType), " ")
+ debug, p.Root, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager, p.TmpDir, eventsType), " ")
if os.Getenv("HOOK_OPTION") != "" {
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
}
diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go
index 6bc230aae..2a6f177f2 100644
--- a/test/e2e/containers_conf_test.go
+++ b/test/e2e/containers_conf_test.go
@@ -245,7 +245,7 @@ var _ = Describe("Podman run", func() {
session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputStartsWith("search")).To(BeFalse())
+ Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("search"))))
})
It("podman run use containers.conf search domain", func() {
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index 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_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 2ecbd0eab..d60383029 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -148,7 +148,7 @@ func (p *PodmanTestIntegration) StopRemoteService() {
//MakeOptions assembles all the podman main options
func getRemoteOptions(p *PodmanTestIntegration, args []string) []string {
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s",
- p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
+ p.Root, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
if os.Getenv("HOOK_OPTION") != "" {
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
}
@@ -164,15 +164,16 @@ func (p *PodmanTestIntegration) SeedImages() error {
// RestoreArtifact puts the cached image into our test store
func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
- fmt.Printf("Restoring %s...\n", image)
- dest := strings.Split(image, "/")
- destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
- args := []string{"load", "-q", "-i", destName}
- podmanOptions := getRemoteOptions(p, args)
- command := exec.Command(p.PodmanBinary, podmanOptions...)
- fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
- command.Start()
- command.Wait()
+ tarball := imageTarPath(image)
+ if _, err := os.Stat(tarball); err == nil {
+ fmt.Printf("Restoring %s...\n", image)
+ args := []string{"load", "-q", "-i", tarball}
+ podmanOptions := getRemoteOptions(p, args)
+ command := exec.Command(p.PodmanBinary, podmanOptions...)
+ fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
+ command.Start()
+ command.Wait()
+ }
return nil
}
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 001a869b1..4147ba2c3 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -7,7 +7,6 @@ import (
"io/ioutil"
"os"
"path/filepath"
- "strings"
"github.com/containers/podman/v3/pkg/rootless"
)
@@ -59,11 +58,12 @@ func PodmanTestCreate(tempDir string) *PodmanTestIntegration {
// RestoreArtifact puts the cached image into our test store
func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
- fmt.Printf("Restoring %s...\n", image)
- dest := strings.Split(image, "/")
- destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
- restore := p.PodmanNoEvents([]string{"load", "-q", "-i", destName})
- restore.Wait(90)
+ tarball := imageTarPath(image)
+ if _, err := os.Stat(tarball); err == nil {
+ fmt.Printf("Restoring %s...\n", image)
+ restore := p.PodmanNoEvents([]string{"load", "-q", "-i", tarball})
+ restore.Wait(90)
+ }
return nil
}
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/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 734a45bca..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() {
@@ -209,7 +208,7 @@ var _ = Describe("Podman network", func() {
results := podmanTest.Podman([]string{"network", "ls", "--quiet"})
results.WaitWithDefaultTimeout()
Expect(results).Should(Exit(0))
- Expect(results.LineInOutputContains(name)).To(BeFalse())
+ Expect(results.OutputToString()).To(Not(ContainSubstring(name)))
})
}
@@ -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/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_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 c0b0e0aa6..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())
diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go
index 7038a09e8..a3b5e31bb 100644
--- a/test/e2e/push_test.go
+++ b/test/e2e/push_test.go
@@ -2,12 +2,14 @@ package integration
import (
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/containers/podman/v3/pkg/rootless"
. "github.com/containers/podman/v3/test/utils"
+ "github.com/containers/storage/pkg/archive"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
@@ -63,6 +65,36 @@ var _ = Describe("Podman push", func() {
Expect(session).Should(Exit(0))
})
+ It("podman push to oci with compression-format", func() {
+ SkipIfRemote("Remote push does not support dir transport")
+ bbdir := filepath.Join(podmanTest.TempDir, "busybox-oci")
+ session := podmanTest.Podman([]string{"push", "--compression-format=zstd", "--remove-signatures", ALPINE,
+ fmt.Sprintf("oci:%s", bbdir)})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ foundZstdFile := false
+
+ blobsDir := filepath.Join(bbdir, "blobs/sha256")
+
+ blobs, err := ioutil.ReadDir(blobsDir)
+ Expect(err).To(BeNil())
+
+ for _, f := range blobs {
+ blobPath := filepath.Join(blobsDir, f.Name())
+
+ sourceFile, err := ioutil.ReadFile(blobPath)
+ Expect(err).To(BeNil())
+
+ compressionType := archive.DetectCompression(sourceFile)
+ if compressionType == archive.Zstd {
+ foundZstdFile = true
+ break
+ }
+ }
+ Expect(foundZstdFile).To(BeTrue())
+ })
+
It("podman push to local registry", func() {
SkipIfRemote("Remote does not support --digestfile or --remove-signatures")
if podmanTest.Host.Arch == "ppc64le" {
diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go
index 6a61c1292..f85a74f47 100644
--- a/test/e2e/restart_test.go
+++ b/test/e2e/restart_test.go
@@ -154,8 +154,8 @@ var _ = Describe("Podman restart", func() {
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
timeSince := time.Since(startTime)
- Expect(timeSince < 10*time.Second).To(BeTrue())
- Expect(timeSince > 2*time.Second).To(BeTrue())
+ Expect(timeSince).To(BeNumerically("<", 10*time.Second))
+ Expect(timeSince).To(BeNumerically(">", 2*time.Second))
})
It("Podman restart --all", func() {
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index 4f6d974fd..2c2a9688e 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -276,8 +276,7 @@ RUN find $LOCAL
session := podmanTest.Podman([]string{"image", "rm"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(125))
- match, _ := session.ErrorGrepString("image name or ID must be specified")
- Expect(match).To(BeTrue())
+ Expect(session.ErrorToString()).To(ContainSubstring("image name or ID must be specified"))
})
It("podman image rm - concurrent with shared layers", func() {
diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go
index beb6390e0..8b6d535e5 100644
--- a/test/e2e/run_dns_test.go
+++ b/test/e2e/run_dns_test.go
@@ -44,7 +44,7 @@ var _ = Describe("Podman run dns", func() {
session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputStartsWith("search")).To(BeFalse())
+ Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("search"))))
})
It("podman run add bad dns server", func() {
diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go
index f500a3c7c..29f76bad1 100644
--- a/test/e2e/run_entrypoint_test.go
+++ b/test/e2e/run_entrypoint_test.go
@@ -129,6 +129,6 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest", "-r"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputStartsWith("Linux")).To(BeFalse())
+ Expect(session.OutputToStringArray()).To(Not(ContainElement(HavePrefix("Linux"))))
})
})
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 5898cc38d..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() {
@@ -866,7 +865,6 @@ EXPOSE 2004-2005/tcp`, ALPINE)
inspectOut := podmanTest.InspectContainer(ctrName)
Expect(len(inspectOut)).To(Equal(1))
Expect(len(inspectOut[0].NetworkSettings.Networks)).To(Equal(1))
- _, ok := inspectOut[0].NetworkSettings.Networks["podman"]
- Expect(ok).To(BeTrue())
+ Expect(inspectOut[0].NetworkSettings.Networks).To(HaveKey("podman"))
})
})
diff --git a/test/e2e/run_passwd_test.go b/test/e2e/run_passwd_test.go
index 05cdc7d80..6d1d26914 100644
--- a/test/e2e/run_passwd_test.go
+++ b/test/e2e/run_passwd_test.go
@@ -38,20 +38,20 @@ var _ = Describe("Podman run passwd", func() {
session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("passwd")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("passwd")))
})
It("podman run user specified in container", func() {
session := podmanTest.Podman([]string{"run", "--read-only", "-u", "bin", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("passwd")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("passwd")))
})
It("podman run UID specified in container", func() {
session := podmanTest.Podman([]string{"run", "--read-only", "-u", "2:1", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("passwd")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("passwd")))
})
It("podman run UID not specified in container", func() {
@@ -77,14 +77,14 @@ USER 1000`, ALPINE)
session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("/etc/group")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group")))
})
It("podman run group specified in container", func() {
session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:bin", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("/etc/group")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group")))
})
It("podman run non-numeric group not specified in container", func() {
@@ -97,7 +97,7 @@ USER 1000`, ALPINE)
session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:11", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("/etc/group")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group")))
})
It("podman run numeric group not specified in container", func() {
diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go
index 321bf27ac..f1084a3d2 100644
--- a/test/e2e/run_privileged_test.go
+++ b/test/e2e/run_privileged_test.go
@@ -63,9 +63,7 @@ var _ = Describe("Podman privileged container tests", func() {
session := podmanTest.Podman([]string{"run", "--privileged", BB, "mount"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- ok, lines := session.GrepString("sysfs")
- Expect(ok).To(BeTrue())
- Expect(lines[0]).To(ContainSubstring("sysfs (rw,"))
+ Expect(session.OutputToString()).To(ContainSubstring("sysfs (rw,"))
})
It("podman privileged CapEff", func() {
diff --git a/test/e2e/run_signal_test.go b/test/e2e/run_signal_test.go
index 49f456366..6bb325155 100644
--- a/test/e2e/run_signal_test.go
+++ b/test/e2e/run_signal_test.go
@@ -116,8 +116,7 @@ var _ = Describe("Podman run with --sig-proxy", func() {
}
session, pid := podmanTest.PodmanPID([]string{"run", "--name", "test2", "--sig-proxy=false", fedoraMinimal, "bash", "-c", sigCatch2})
- ok := WaitForContainer(podmanTest)
- Expect(ok).To(BeTrue())
+ Expect(WaitForContainer(podmanTest)).To(BeTrue(), "WaitForContainer()")
// Kill with given signal
// Should be no output, SIGPOLL is usually ignored
@@ -132,8 +131,7 @@ var _ = Describe("Podman run with --sig-proxy", func() {
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
- ok, _ = session.GrepString("Received")
- Expect(ok).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("Received")))
})
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 7b08c48c3..3d4c1240e 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -871,7 +871,7 @@ USER bin`, BB)
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- Expect(session.LineInOutputContains("27(video),777,65533(nogroup)")).To(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("27(video),777,65533(nogroup)")))
})
It("podman run with group-add", func() {
@@ -1151,8 +1151,7 @@ USER mail`, BB)
session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:z", "--volume", vol2 + ":/myvol2:z", fedoraMinimal, "findmnt", "-o", "TARGET,PROPAGATION"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- match, _ := session.GrepString("shared")
- Expect(match).Should(BeFalse())
+ Expect(session.OutputToString()).To(Not(ContainSubstring("shared")))
})
It("podman run findmnt shared", func() {
@@ -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 196c5778a..74aa69c96 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -103,10 +103,8 @@ var _ = Describe("Podman run with volumes", func() {
session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",consistency=delegated,shared", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- found, matches := session.GrepString(dest)
- Expect(found).Should(BeTrue())
- Expect(matches[0]).To(ContainSubstring("rw"))
- Expect(matches[0]).To(ContainSubstring("shared"))
+ Expect(session.OutputToString()).To(ContainSubstring("rw"))
+ Expect(session.OutputToString()).To(ContainSubstring("shared"))
session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
@@ -195,20 +193,18 @@ var _ = Describe("Podman run with volumes", func() {
session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- found, matches := session.GrepString(dest)
- Expect(found).Should(BeTrue())
- Expect(matches[0]).To(Not(ContainSubstring("noexec")))
- Expect(matches[0]).To(Not(ContainSubstring("nodev")))
- Expect(matches[0]).To(Not(ContainSubstring("nosuid")))
+ output := session.OutputToString()
+ Expect(output).To(Not(ContainSubstring("noexec")))
+ Expect(output).To(Not(ContainSubstring("nodev")))
+ Expect(output).To(Not(ContainSubstring("nosuid")))
session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- found, matches = session.GrepString(dest)
- Expect(found).Should(BeTrue())
- Expect(matches[0]).To(Not(ContainSubstring("noexec")))
- Expect(matches[0]).To(Not(ContainSubstring("nodev")))
- Expect(matches[0]).To(Not(ContainSubstring("nosuid")))
+ output = session.OutputToString()
+ Expect(output).To(Not(ContainSubstring("noexec")))
+ Expect(output).To(Not(ContainSubstring("nodev")))
+ Expect(output).To(Not(ContainSubstring("nosuid")))
})
// Container should start when workdir is overlay volume
@@ -287,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"})
@@ -303,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})
@@ -324,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() {
@@ -516,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() {
@@ -752,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})
@@ -762,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 e473119b2..b7b27dc14 100644
--- a/test/e2e/runlabel_test.go
+++ b/test/e2e/runlabel_test.go
@@ -94,14 +94,14 @@ 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() {
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 df0cd24d0..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() {
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 6b4df939c..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
`
})
@@ -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()
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_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/system/010-images.bats b/test/system/010-images.bats
index 1e9d5f181..9de31f96c 100644
--- a/test/system/010-images.bats
+++ b/test/system/010-images.bats
@@ -221,9 +221,7 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
iid=${output:0:12}
# Run the test: this will output three column-aligned rows. Test them.
- # Tab character (\t) should have the same effect as the 'table' directive
_run_format_test 'table' 'table {{.Repository}} {{.Tag}} {{.ID}}'
- _run_format_test 'tabs' '{{.Repository}}\t{{.Tag}}\t{{.ID}}'
# Clean up.
run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag}
diff --git a/test/system/110-history.bats b/test/system/110-history.bats
index 75c15b088..0f6d75cb3 100644
--- a/test/system/110-history.bats
+++ b/test/system/110-history.bats
@@ -21,6 +21,14 @@ load helpers
done
}
+@test "podman history - custom format" {
+ run_podman history --format "{{.ID}}\t{{.ID}}" $IMAGE
+ od -c <<<$output
+ while IFS= read -r row; do
+ is "$row" ".* .*$"
+ done <<<$output
+}
+
@test "podman history - json" {
# Sigh. Timestamp in .created can be '...Z' or '...-06:00'
tests="
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/utils/matchers.go b/test/utils/matchers.go
index 69fb0cdfe..288779b63 100644
--- a/test/utils/matchers.go
+++ b/test/utils/matchers.go
@@ -1,6 +1,7 @@
package utils
import (
+ "encoding/json"
"fmt"
"net/url"
@@ -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 944c1ac3c..f41024072 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -36,7 +36,6 @@ type PodmanTestCommon interface {
type PodmanTest struct {
PodmanMakeOptions func(args []string, noEvents, noCache bool) []string
PodmanBinary string
- ArtifactPath string
TempDir string
RemoteTest bool
RemotePodmanBinary string
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