diff options
58 files changed, 535 insertions, 317 deletions
@@ -478,6 +478,7 @@ podman-remote-release-%.zip: cp release.txt "$(TMPDIR)/" cp ./bin/podman-remote-$*$(BINSFX) "$(TMPDIR)/$(SUBDIR)/podman$(BINSFX)" cp -r ./docs/build/remote/$* "$(TMPDIR)/$(SUBDIR)/docs/" + cp ./contrib/remote/containers.conf "$(TMPDIR)/$(SUBDIR)/" cd "$(TMPDIR)/$(SUBDIR)" && \ zip --recurse-paths "$(CURDIR)/$@" "./release.txt" "./" -rm -rf "$(TMPDIR)" diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index fef059958..1f6fbbe98 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -520,7 +520,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string case "label": // TODO selinux opts and label opts are the same thing s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) - s.Annotations[define.InspectAnnotationLabel] = con[1] + s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") case "apparmor": s.ContainerSecurityConfig.ApparmorProfile = con[1] s.Annotations[define.InspectAnnotationApparmor] = con[1] diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 45ce00c86..c48a739ff 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -6,11 +6,12 @@ import ( "os" "strings" - "github.com/containers/libpod/libpod/define" - "github.com/containers/common/pkg/config" + "github.com/containers/image/v5/storage" + "github.com/containers/image/v5/transports/alltransports" "github.com/containers/libpod/cmd/podman/common" "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/errorhandling" "github.com/containers/libpod/pkg/specgen" @@ -108,12 +109,15 @@ func create(cmd *cobra.Command, args []string) error { return err } + imageName := args[0] if !cliVals.RootFS { - if err := pullImage(args[0]); err != nil { + name, err := pullImage(args[0]) + if err != nil { return err } + imageName = name } - s := specgen.NewSpecGenerator(args[0], cliVals.RootFS) + s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } @@ -211,30 +215,44 @@ func createInit(c *cobra.Command) error { return nil } -func pullImage(imageName string) error { - br, err := registry.ImageEngine().Exists(registry.GetContext(), imageName) - if err != nil { - return err - } +func pullImage(imageName string) (string, error) { pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) if err != nil { - return err + return "", err } - if !br.Value || pullPolicy == config.PullImageAlways { + + // Check if the image is missing and hence if we need to pull it. + imageMissing := true + imageRef, err := alltransports.ParseImageName(imageName) + switch { + case err != nil: + // Assume we specified a local image withouth the explicit storage transport. + fallthrough + + case imageRef.Transport().Name() == storage.Transport.Name(): + br, err := registry.ImageEngine().Exists(registry.GetContext(), imageName) + if err != nil { + return "", err + } + imageMissing = !br.Value + } + + if imageMissing || pullPolicy == config.PullImageAlways { if pullPolicy == config.PullImageNever { - return errors.Wrapf(define.ErrNoSuchImage, "unable to find a name and tag match for %s in repotags", imageName) + return "", errors.Wrapf(define.ErrNoSuchImage, "unable to find a name and tag match for %s in repotags", imageName) } - _, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ + pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ Authfile: cliVals.Authfile, Quiet: cliVals.Quiet, OverrideArch: cliVals.OverrideArch, OverrideOS: cliVals.OverrideOS, }) if pullErr != nil { - return pullErr + return "", pullErr } + imageName = pullReport.Images[0] } - return nil + return imageName, nil } func openCidFile(cidfile string) (*os.File, error) { diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index cb307c38f..4b287838e 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -125,10 +125,13 @@ func run(cmd *cobra.Command, args []string) error { return err } + imageName := args[0] if !cliVals.RootFS { - if err := pullImage(args[0]); err != nil { + name, err := pullImage(args[0]) + if err != nil { return err } + imageName = name } if cliVals.Replace { @@ -166,7 +169,7 @@ func run(cmd *cobra.Command, args []string) error { runOpts.Detach = cliVals.Detach runOpts.DetachKeys = cliVals.DetachKeys cliVals.PreserveFDs = runOpts.PreserveFDs - s := specgen.NewSpecGenerator(args[0], cliVals.RootFS) + s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { return err } @@ -196,7 +199,7 @@ func run(cmd *cobra.Command, args []string) error { return nil } if runRmi { - _, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), []string{args[0]}, entities.ImageRemoveOptions{}) + _, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), []string{imageName}, entities.ImageRemoveOptions{}) if len(rmErrors) > 0 { logrus.Errorf("%s", errors.Wrapf(errorhandling.JoinErrors(rmErrors), "failed removing image")) } diff --git a/cmd/podman/healthcheck/run.go b/cmd/podman/healthcheck/run.go index 5612910cb..17ddf17b6 100644 --- a/cmd/podman/healthcheck/run.go +++ b/cmd/podman/healthcheck/run.go @@ -12,12 +12,13 @@ import ( var ( healthcheckRunDescription = "run the health check of a container" healthcheckrunCommand = &cobra.Command{ - Use: "run [flags] CONTAINER", - Short: "run the health check of a container", - Long: healthcheckRunDescription, - Example: `podman healthcheck run mywebapp`, - RunE: run, - Args: cobra.ExactArgs(1), + Use: "run CONTAINER", + Short: "run the health check of a container", + Long: healthcheckRunDescription, + Example: `podman healthcheck run mywebapp`, + RunE: run, + Args: cobra.ExactArgs(1), + DisableFlagsInUseLine: true, } ) diff --git a/cmd/podman/images/tag.go b/cmd/podman/images/tag.go index dae3416c4..859489552 100644 --- a/cmd/podman/images/tag.go +++ b/cmd/podman/images/tag.go @@ -9,22 +9,24 @@ import ( var ( tagDescription = "Adds one or more additional names to locally-stored image." tagCommand = &cobra.Command{ - Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]", - Short: "Add an additional name to a local image", - Long: tagDescription, - RunE: tag, - Args: cobra.MinimumNArgs(2), + Use: "tag IMAGE TARGET_NAME [TARGET_NAME...]", + Short: "Add an additional name to a local image", + Long: tagDescription, + RunE: tag, + Args: cobra.MinimumNArgs(2), + DisableFlagsInUseLine: true, Example: `podman tag 0e3bbc2 fedora:latest podman tag imageID:latest myNewImage:newTag podman tag httpd myregistryhost:5000/fedora/httpd:v2`, } imageTagCommand = &cobra.Command{ - Args: tagCommand.Args, - Use: tagCommand.Use, - Short: tagCommand.Short, - Long: tagCommand.Long, - RunE: tagCommand.RunE, + Args: tagCommand.Args, + DisableFlagsInUseLine: true, + Use: tagCommand.Use, + Short: tagCommand.Short, + Long: tagCommand.Long, + RunE: tagCommand.RunE, Example: `podman image tag 0e3bbc2 fedora:latest podman image tag imageID:latest myNewImage:newTag podman image tag httpd myregistryhost:5000/fedora/httpd:v2`, diff --git a/cmd/podman/images/untag.go b/cmd/podman/images/untag.go index 266a3f115..5d1274895 100644 --- a/cmd/podman/images/untag.go +++ b/cmd/podman/images/untag.go @@ -8,22 +8,24 @@ import ( var ( untagCommand = &cobra.Command{ - Use: "untag [flags] IMAGE [NAME...]", - Short: "Remove a name from a local image", - Long: "Removes one or more names from a locally-stored image.", - RunE: untag, - Args: cobra.MinimumNArgs(1), + Use: "untag IMAGE [NAME...]", + Short: "Remove a name from a local image", + Long: "Removes one or more names from a locally-stored image.", + RunE: untag, + Args: cobra.MinimumNArgs(1), + DisableFlagsInUseLine: true, Example: `podman untag 0e3bbc2 podman untag imageID:latest otherImageName:latest podman untag httpd myregistryhost:5000/fedora/httpd:v2`, } imageUntagCommand = &cobra.Command{ - Args: untagCommand.Args, - Use: untagCommand.Use, - Short: untagCommand.Short, - Long: untagCommand.Long, - RunE: untagCommand.RunE, + Args: untagCommand.Args, + DisableFlagsInUseLine: true, + Use: untagCommand.Use, + Short: untagCommand.Short, + Long: untagCommand.Long, + RunE: untagCommand.RunE, Example: `podman image untag 0e3bbc2 podman image untag imageID:latest otherImageName:latest podman image untag httpd myregistryhost:5000/fedora/httpd:v2`, diff --git a/cmd/podman/manifest/inspect.go b/cmd/podman/manifest/inspect.go index 5112aa5b2..861f4be4f 100644 --- a/cmd/podman/manifest/inspect.go +++ b/cmd/podman/manifest/inspect.go @@ -12,12 +12,13 @@ import ( var ( inspectCmd = &cobra.Command{ - Use: "inspect [flags] IMAGE", - Short: "Display the contents of a manifest list or image index", - Long: "Display the contents of a manifest list or image index.", - RunE: inspect, - Example: "podman manifest inspect localhost/list", - Args: cobra.ExactArgs(1), + Use: "inspect IMAGE", + Short: "Display the contents of a manifest list or image index", + Long: "Display the contents of a manifest list or image index.", + RunE: inspect, + Example: "podman manifest inspect localhost/list", + Args: cobra.ExactArgs(1), + DisableFlagsInUseLine: true, } ) diff --git a/cmd/podman/manifest/remove.go b/cmd/podman/manifest/remove.go index 4d345efc0..815a3f0a8 100644 --- a/cmd/podman/manifest/remove.go +++ b/cmd/podman/manifest/remove.go @@ -12,12 +12,13 @@ import ( var ( removeCmd = &cobra.Command{ - Use: "remove [flags] LIST IMAGE", - Short: "Remove an entry from a manifest list or image index", - Long: "Removes an image from a manifest list or image index.", - RunE: remove, - Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, - Args: cobra.ExactArgs(2), + Use: "remove LIST IMAGE", + Short: "Remove an entry from a manifest list or image index", + Long: "Removes an image from a manifest list or image index.", + RunE: remove, + Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, + Args: cobra.ExactArgs(2), + DisableFlagsInUseLine: true, } ) diff --git a/cmd/podman/registry/config_tunnel.go b/cmd/podman/registry/config_tunnel.go index 4f9f51163..bb3da947e 100644 --- a/cmd/podman/registry/config_tunnel.go +++ b/cmd/podman/registry/config_tunnel.go @@ -2,13 +2,6 @@ package registry -import ( - "os" -) - func init() { abiSupport = false - - // Enforce that podman-remote == podman --remote - os.Args = append(os.Args, "--remote") } diff --git a/cmd/podman/system/renumber.go b/cmd/podman/system/renumber.go index b90640de3..39cd15dd7 100644 --- a/cmd/podman/system/renumber.go +++ b/cmd/podman/system/renumber.go @@ -22,11 +22,12 @@ var ( ` renumberCommand = &cobra.Command{ - Use: "renumber", - Args: validate.NoArgs, - Short: "Migrate lock numbers", - Long: renumberDescription, - Run: renumber, + Use: "renumber", + Args: validate.NoArgs, + DisableFlagsInUseLine: true, + Short: "Migrate lock numbers", + Long: renumberDescription, + Run: renumber, } ) diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go index e5d41e06d..109bab9ed 100644 --- a/cmd/podman/system/unshare.go +++ b/cmd/podman/system/unshare.go @@ -13,10 +13,11 @@ import ( var ( unshareDescription = "Runs a command in a modified user namespace." unshareCommand = &cobra.Command{ - Use: "unshare [flags] [COMMAND [ARG ...]]", - Short: "Run a command in a modified user namespace", - Long: unshareDescription, - RunE: unshare, + Use: "unshare [COMMAND [ARG ...]]", + DisableFlagsInUseLine: true, + Short: "Run a command in a modified user namespace", + Long: unshareDescription, + RunE: unshare, Example: `podman unshare id podman unshare cat /proc/self/uid_map, podman unshare podman-script.sh`, diff --git a/contrib/remote/containers.conf b/contrib/remote/containers.conf new file mode 100644 index 000000000..45f58171a --- /dev/null +++ b/contrib/remote/containers.conf @@ -0,0 +1,11 @@ +# The containers configuration file specifies all of the available configuration +# command-line options/flags for container engine tools like Podman +# but in a TOML format that can be easily modified and versioned. + +[engine] + +# Default Remote URI to access the Podman service. +# Examples: +# remote rootless ssh://engineering.lab.company.com/run/user/1000/podman/podman.sock +# remote rootfull ssh://root@10.10.1.136:22/run/podman/podman.sock +# remote_uri= "" diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md index 90e581e42..f37280cda 100644 --- a/docs/source/markdown/podman-auto-update.1.md +++ b/docs/source/markdown/podman-auto-update.1.md @@ -4,7 +4,7 @@ podman-auto-update - Auto update containers according to their auto-update policy ## SYNOPSIS -**podman auto-update** +**podman auto-update** [*options*] ## DESCRIPTION `podman auto-update` looks up containers with a specified "io.containers.autoupdate" label (i.e., the auto-update policy). diff --git a/docs/source/markdown/podman-container-exists.1.md b/docs/source/markdown/podman-container-exists.1.md index 3b4ca33e4..d24df2fc8 100644 --- a/docs/source/markdown/podman-container-exists.1.md +++ b/docs/source/markdown/podman-container-exists.1.md @@ -4,7 +4,7 @@ podman-container-exists - Check if a container exists in local storage ## SYNOPSIS -**podman container exists** [*options*] *container* +**podman container exists** *container* ## DESCRIPTION **podman container exists** checks if a container exists in local storage. The **ID** or **Name** diff --git a/docs/source/markdown/podman-healthcheck-run.1.md b/docs/source/markdown/podman-healthcheck-run.1.md index 21f2d9b20..546d847eb 100644 --- a/docs/source/markdown/podman-healthcheck-run.1.md +++ b/docs/source/markdown/podman-healthcheck-run.1.md @@ -4,7 +4,7 @@ podman\-healthcheck\-run - Run a container healthcheck ## SYNOPSIS -**podman healthcheck run** [*options*] *container* +**podman healthcheck run** *container* ## DESCRIPTION diff --git a/docs/source/markdown/podman-image-exists.1.md b/docs/source/markdown/podman-image-exists.1.md index 3b7127b64..59f2145cc 100644 --- a/docs/source/markdown/podman-image-exists.1.md +++ b/docs/source/markdown/podman-image-exists.1.md @@ -4,7 +4,7 @@ podman-image-exists - Check if an image exists in local storage ## SYNOPSIS -**podman image exists** [*options*] *image* +**podman image exists** *image* ## DESCRIPTION **podman image exists** checks if an image exists in local storage. The **ID** or **Name** diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md index 82f2071b9..44815def5 100644 --- a/docs/source/markdown/podman-manifest-add.1.md +++ b/docs/source/markdown/podman-manifest-add.1.md @@ -4,7 +4,7 @@ podman\-manifest\-add - Add an image to a manifest list or image index ## SYNOPSIS -**podman manifest add** *listnameorindexname* *imagename* +**podman manifest add** [*options*] *listnameorindexname* *imagename* ## DESCRIPTION diff --git a/docs/source/markdown/podman-manifest-annotate.1.md b/docs/source/markdown/podman-manifest-annotate.1.md index 4450de7fd..25ad4642e 100644 --- a/docs/source/markdown/podman-manifest-annotate.1.md +++ b/docs/source/markdown/podman-manifest-annotate.1.md @@ -4,7 +4,7 @@ podman\-manifest\-annotate - Add or update information about an entry in a manifest list or image index ## SYNOPSIS -**podman manifest annotate** [options...] *listnameorindexname* *imagemanifestdigest* +**podman manifest annotate** [*options*] *listnameorindexname* *imagemanifestdigest* ## DESCRIPTION diff --git a/docs/source/markdown/podman-manifest-push.1.md b/docs/source/markdown/podman-manifest-push.1.md index ab3287a7c..33b2a24c5 100644 --- a/docs/source/markdown/podman-manifest-push.1.md +++ b/docs/source/markdown/podman-manifest-push.1.md @@ -4,7 +4,7 @@ podman\-manifest\-push - Push a manifest list or image index to a registry ## SYNOPSIS -**podman manifest push** [options...] *listnameorindexname* *transport:details* +**podman manifest push** [*options*] *listnameorindexname* *transport:details* ## DESCRIPTION Pushes a manifest list or image index to a registry. diff --git a/docs/source/markdown/podman-mount.1.md b/docs/source/markdown/podman-mount.1.md index c7bfedb48..eaed1051e 100644 --- a/docs/source/markdown/podman-mount.1.md +++ b/docs/source/markdown/podman-mount.1.md @@ -4,9 +4,9 @@ podman\-mount - Mount a working container's root filesystem ## SYNOPSIS -**podman mount** [*container* ...] +**podman mount** [*options*] [*container* ...] -**podman container mount** [*container* ...] +**podman container mount** [*options*] [*container* ...] ## DESCRIPTION Mounts the specified containers' root file system in a location which can be diff --git a/docs/source/markdown/podman-network-inspect.1.md b/docs/source/markdown/podman-network-inspect.1.md index 86fa2552e..c75c6788a 100644 --- a/docs/source/markdown/podman-network-inspect.1.md +++ b/docs/source/markdown/podman-network-inspect.1.md @@ -4,7 +4,7 @@ podman\-network\-inspect - Displays the raw CNI network configuration for one or more networks ## SYNOPSIS -**podman network inspect** [*network* ...] +**podman network inspect** [*options*] [*network* ...] ## DESCRIPTION Display the raw (JSON format) network configuration. This command is not available for rootless users. diff --git a/docs/source/markdown/podman-network-rm.1.md b/docs/source/markdown/podman-network-rm.1.md index c71f0d8fd..9ce4d1cd8 100644 --- a/docs/source/markdown/podman-network-rm.1.md +++ b/docs/source/markdown/podman-network-rm.1.md @@ -4,7 +4,7 @@ podman\-network\-rm - Remove one or more CNI networks ## SYNOPSIS -**podman network rm** [*network...*] +**podman network rm** [*options*] [*network...*] ## DESCRIPTION Delete one or more Podman networks. diff --git a/docs/source/markdown/podman-pod-prune.1.md b/docs/source/markdown/podman-pod-prune.1.md index 5b74adade..5b4c4661c 100644 --- a/docs/source/markdown/podman-pod-prune.1.md +++ b/docs/source/markdown/podman-pod-prune.1.md @@ -4,7 +4,7 @@ podman-pod-prune - Remove all stopped pods and their containers ## SYNOPSIS -**podman pod prune** +**podman pod prune** [*options*] ## DESCRIPTION **podman pod prune** removes all stopped pods and their containers from local storage. diff --git a/docs/source/markdown/podman-rmi.1.md b/docs/source/markdown/podman-rmi.1.md index 2e093e9c8..58280e831 100644 --- a/docs/source/markdown/podman-rmi.1.md +++ b/docs/source/markdown/podman-rmi.1.md @@ -4,9 +4,9 @@ podman\-rmi - Removes one or more locally stored images ## SYNOPSIS -**podman rmi** *image* [...] +**podman rmi** [*options*] *image* [...] -**podman image rm** *image* [...] +**podman image rm** [*options*] *image* [...] ## DESCRIPTION Removes one or more locally stored images. diff --git a/docs/source/markdown/podman-system-migrate.1.md b/docs/source/markdown/podman-system-migrate.1.md index 28db56dee..baabfd14b 100644 --- a/docs/source/markdown/podman-system-migrate.1.md +++ b/docs/source/markdown/podman-system-migrate.1.md @@ -4,7 +4,7 @@ podman\-system\-migrate - Migrate existing containers to a new podman version ## SYNOPSIS -**podman system migrate** +**podman system migrate** [*options*] ## DESCRIPTION **podman system migrate** migrates containers to the latest podman version. diff --git a/docs/source/markdown/podman-system-reset.1.md b/docs/source/markdown/podman-system-reset.1.md index 432f275f4..f290e26d5 100644 --- a/docs/source/markdown/podman-system-reset.1.md +++ b/docs/source/markdown/podman-system-reset.1.md @@ -4,7 +4,7 @@ podman\-system\-reset - Reset storage back to initial state ## SYNOPSIS -**podman system reset** +**podman system reset** [*options*] ## DESCRIPTION **podman system reset** removes all pods, containers, images and volumes. diff --git a/docs/source/markdown/podman-umount.1.md b/docs/source/markdown/podman-umount.1.md index 100c47b32..31a213f28 100644 --- a/docs/source/markdown/podman-umount.1.md +++ b/docs/source/markdown/podman-umount.1.md @@ -4,13 +4,13 @@ podman\-umount - Unmount a working container's root filesystem ## SYNOPSIS -**podman umount** *container* [...] +**podman umount** [*options*] *container* [...] -**podman container umount** *container* [...] +**podman container umount** [*options*] *container* [...] -**podman container unmount** *container* [...] +**podman container unmount** [*options*] *container* [...] -**podman unmount** *container* [...] +**podman unmount** [*options*] *container* [...] ## DESCRIPTION Unmounts the specified containers' root file system, if no other processes diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md index f2eb02814..239213981 100644 --- a/docs/source/markdown/podman-unshare.1.md +++ b/docs/source/markdown/podman-unshare.1.md @@ -4,7 +4,7 @@ podman\-unshare - Run a command inside of a modified user namespace ## SYNOPSIS -**podman unshare** [*options*] [*--*] [*command*] +**podman unshare** [*--*] [*command*] ## DESCRIPTION Launches a process (by default, *$SHELL*) in a new user namespace. The user diff --git a/docs/source/markdown/podman-untag.1.md b/docs/source/markdown/podman-untag.1.md index c83a0544c..d6ed7f3ea 100644 --- a/docs/source/markdown/podman-untag.1.md +++ b/docs/source/markdown/podman-untag.1.md @@ -4,9 +4,9 @@ podman\-untag - Removes one or more names from a locally-stored image ## SYNOPSIS -**podman untag** [*options*] *image* [*name*[:*tag*]...] +**podman untag** *image* [*name*[:*tag*]...] -**podman image untag** [*options*] *image* [*name*[:*tag*]...] +**podman image untag** *image* [*name*[:*tag*]...] ## DESCRIPTION Remove one or more names from an image in the local storage. The image can be referred to by ID or reference. If a no name is specified, all names are removed the image. If a specified name is a short name and does not include a registry `localhost/` will be prefixed (e.g., `fedora` -> `localhost/fedora`). If a specified name does not include a tag `:latest` will be appended (e.g., `localhost/fedora` -> `localhost/fedora:latest`). @@ -11,7 +11,7 @@ require ( github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921 github.com/containernetworking/plugins v0.8.6 github.com/containers/buildah v1.15.0 - github.com/containers/common v0.14.0 + github.com/containers/common v0.14.3 github.com/containers/conmon v2.0.18+incompatible github.com/containers/image/v5 v5.5.1 github.com/containers/psgo v1.5.1 @@ -70,6 +70,8 @@ github.com/containers/buildah v1.15.0 h1:p9cYJwcQ5Fnv0iBeHAFwHR0K+kcv7LbyAjUtc+H github.com/containers/buildah v1.15.0/go.mod h1:j0AY2kWpmaOPPV5GKDJY9dMtekk5WMmMhcB+z0OW+vc= github.com/containers/common v0.14.0 h1:hiZFDPf6ajKiDmojN5f5X3gboKPO73NLrYb0RXfrQiA= github.com/containers/common v0.14.0/go.mod h1:9olhlE+WhYof1npnMJdyRMX14/yIUint6zyHzcyRVAg= +github.com/containers/common v0.14.3 h1:LNsRPkap5Q/EqPyhiLKRZg8u629U8CEeoB49ilG6ZR4= +github.com/containers/common v0.14.3/go.mod h1:9olhlE+WhYof1npnMJdyRMX14/yIUint6zyHzcyRVAg= github.com/containers/conmon v2.0.18+incompatible h1:rjwjNnE756NuXcdE/uUmj4kDbrykslPuBMHI31wh43E= github.com/containers/conmon v2.0.18+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.4.4/go.mod h1:g7cxNXitiLi6pEr9/L9n/0wfazRuhDKXU15kV86N8h8= diff --git a/hack/man-page-checker b/hack/man-page-checker index 17d85d65d..d2cc6c6e1 100755 --- a/hack/man-page-checker +++ b/hack/man-page-checker @@ -3,6 +3,14 @@ # man-page-checker - validate and cross-reference man page names # +verbose= +for i; do + case "$i" in + -v|--verbose) verbose=verbose ;; + esac +done + + die() { echo "$(basename $0): $*" >&2 exit 1 @@ -65,6 +73,61 @@ for md in $(ls -1 *-*.1.md | grep -v remote);do fi done +# Helper function: compares man page synopsis vs --help usage message +function compare_usage() { + local cmd="$1" + local from_man="$2" + + # Sometimes in CI we run before podman gets built. + test -x ../../../bin/podman || return + + # Run 'cmd --help', grab the line immediately after 'Usage:' + local help_output=$(../../../bin/$cmd --help) + local from_help=$(echo "$help_output" | grep -A1 '^Usage:' | tail -1) + + # strip off command name from both + from_man=$(sed -e "s/\*\*$cmd\*\*[[:space:]]*//" <<<"$from_man") + from_help=$(sed -e "s/^[[:space:]]*$cmd[[:space:]]*//" <<<"$from_help") + + # man page lists 'foo [*options*]', help msg shows 'foo [flags]'. + # Make sure if one has it, the other does too. + if expr "$from_man" : "\[\*options\*\]" >/dev/null; then + if expr "$from_help" : "\[flags\]" >/dev/null; then + : + else + echo "WARNING: $cmd: man page shows '[*options*]', help does not show [flags]" + rc=1 + fi + elif expr "$from_help" : "\[flags\]" >/dev/null; then + echo "WARNING: $cmd: --help shows [flags], man page does not show [*options*]" + rc=1 + fi + + # Strip off options and flags; start comparing arguments + from_man=$(sed -e 's/^\[\*options\*\][[:space:]]*//' <<<"$from_man") + from_help=$(sed -e 's/^\[flags\][[:space:]]*//' <<<"$from_help") + + # Args in man page are '*foo*', in --help are 'FOO'. Convert all to + # UPCASE simply because it stands out better to the eye. + from_man=$(sed -e 's/\*\([a-z-]\+\)\*/\U\1/g' <<<"$from_man") + + # FIXME: one of the common patterns is for --help to show 'POD [POD...]' + # but man page show 'pod ...'. This conversion may help one day, but + # not yet: there are too many inconsistencies such as '[pod ...]' + # (brackets) and 'pod...' (no space between). +# from_help=$(sed -e 's/\([A-Z]\+\)[[:space:]]\+\[\1[[:space:]]*\.\.\.\]/\1 .../' <<<"$from_help") + + # Compare man-page and --help usage strings. For now, do so only + # when run with --verbose. + if [[ "$from_man" != "$from_help" ]]; then + if [ -n "$verbose" ]; then + printf "%-25s man='%s' help='%s'\n" "$cmd:" "$from_man" "$from_help" + # Yeah, we're not going to enable this as a blocker any time soon. + # rc=1 + fi + fi +} + # Pass 3: compare synopses. # # Make sure the SYNOPSIS line in podman-foo.1.md reads '**podman foo** ...' @@ -87,9 +150,7 @@ for md in *.1.md;do cmd=$(echo "$synopsis" | sed -e 's/\(.*\)\*\*.*/\1/' | tr -d \*) md_nodash=$(basename "$md" .1.md | tr '-' ' ') if [[ $md_nodash = 'podman auto update' ]]; then - # podman-auto-update.1.md is special cased as it's structure differs - # from that of other man pages where main and sub-commands split by - # dashes. + # special case: the command is "auto-update", with a hyphen md_nodash='podman auto-update' fi if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then @@ -111,8 +172,9 @@ for md in *.1.md;do # (for debugging, and getting a sense of standard conventions) #printf " %-32s ------ '%s'\n" $md "$synopsis" - # FIXME: some day: run ./bin/podman "args", extract Usage, - # strip off [flags] and [options], then compare arguments + # If bin/podman is available, run "cmd --help" and compare Usage + # messages. This is complicated, so do it in a helper function. + compare_usage "$md_nodash" "$synopsis" done diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go index 8f7016903..dae76c061 100644 --- a/pkg/api/handlers/compat/exec.go +++ b/pkg/api/handlers/compat/exec.go @@ -62,7 +62,8 @@ func ExecCreateHandler(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, false, true, true) + // Automatically log to syslog if the server has log-level=debug set + exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), true, true) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index b07eb7f9a..cbac2cb06 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -145,11 +145,15 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } return nil }) - if batchErr != nil { return entities.ListContainer{}, batchErr } + portMappings, err := ctr.PortMappings() + if err != nil { + return entities.ListContainer{}, err + } + ps := entities.ListContainer{ Command: conConfig.Command, Created: conConfig.CreatedTime.Unix(), @@ -165,7 +169,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities Names: []string{conConfig.Name}, Pid: pid, Pod: conConfig.Pod, - Ports: conConfig.PortMappings, + Ports: portMappings, Size: size, StartedAt: startedTime.Unix(), State: conState.String(), diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index ea6f938a8..7b4fbebf4 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -114,8 +114,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } options = append(options, opts...) - // TODO: Enable syslog support - we'll need to put this in SpecGen. - exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, false, s.Remove, false) + exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) if err != nil { return nil, err } diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 46ff8c716..3d5bf03e5 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -129,7 +129,7 @@ type ContainerBasicConfig struct { Sysctl map[string]string `json:"sysctl,omitempty"` // Remove indicates if the container should be removed once it has been started // and exits - Remove bool `json:"remove"` + Remove bool `json:"remove,omitempty"` // PreserveFDs is a number of additional file descriptors (in addition // to 0, 1, 2) that will be passed to the executed process. The total FDs // passed will be 3 + PreserveFDs. diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index bf6cb81b8..333f8ef88 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -69,8 +69,6 @@ type containerInfo struct { const containerTemplate = headerTemplate + ` {{- if .BoundToServices}} -RefuseManualStart=yes -RefuseManualStop=yes BindsTo={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $value }}.service{{end}} {{- end}} diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 80f0996a1..e108251ea 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -88,8 +88,6 @@ Description=Podman container-foobar.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target -RefuseManualStart=yes -RefuseManualStop=yes BindsTo=a.service b.service c.service pod.service After=a.service b.service c.service pod.service diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 0dc8e01af..cfc0a415e 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -449,4 +449,21 @@ var _ = Describe("Podman ps", func() { Expect(len(output)).To(Equal(1)) Expect(output[0]).To(Equal(ctrName)) }) + + It("podman ps test with port shared with pod", func() { + podName := "testPod" + pod := podmanTest.Podman([]string{"pod", "create", "-p", "8080:80", "--name", podName}) + pod.WaitWithDefaultTimeout() + Expect(pod.ExitCode()).To(Equal(0)) + + ctrName := "testCtr" + session := podmanTest.Podman([]string{"run", "--name", ctrName, "-dt", "--pod", podName, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{.Ports}}"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps.OutputToString()).To(ContainSubstring("0.0.0.0:8080->80/tcp")) + }) }) diff --git a/test/python/dockerpy/README.md b/test/python/dockerpy/README.md new file mode 100644 index 000000000..22908afc6 --- /dev/null +++ b/test/python/dockerpy/README.md @@ -0,0 +1,40 @@ +# Dockerpy regression test + +Python test suite to validate Podman endpoints using dockerpy library + +## Running Tests + +To run the tests locally in your sandbox (Fedora 32): + +```shell script +# dnf install python3-docker +``` + +### Run the entire test suite + +```shell +# cd test/python/dockerpy +# PYTHONPATH=/usr/bin/python python -m unittest discover . +``` + +Passing the -v option to your test script will instruct unittest.main() to enable a higher level of verbosity, and produce detailed output: + +```shell +# cd test/python/dockerpy +# PYTHONPATH=/usr/bin/python python -m unittest -v discover . +``` + +### Run a specific test class + +```shell +# cd test/python/dockerpy +# PYTHONPATH=/usr/bin/python python -m unittest -v tests.test_images +``` + +### Run a specific test within the test class + +```shell +# cd test/python/dockerpy +# PYTHONPATH=/usr/bin/python python -m unittest tests.test_images.TestImages.test_import_image + +``` diff --git a/test/test_dockerpy/__init__.py b/test/python/dockerpy/__init__.py index e69de29bb..e69de29bb 100644 --- a/test/test_dockerpy/__init__.py +++ b/test/python/dockerpy/__init__.py diff --git a/test/python/dockerpy/tests/__init__.py b/test/python/dockerpy/tests/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/python/dockerpy/tests/__init__.py diff --git a/test/python/dockerpy/tests/common.py b/test/python/dockerpy/tests/common.py new file mode 100644 index 000000000..f83f4076f --- /dev/null +++ b/test/python/dockerpy/tests/common.py @@ -0,0 +1,105 @@ +import os +import pathlib +import subprocess +import sys +import time + +from docker import APIClient + +from . import constant + +alpineDict = { + "name": "docker.io/library/alpine:latest", + "shortName": "alpine", + "tarballName": "alpine.tar" +} + + +def get_client(): + client = APIClient(base_url="http://localhost:8080", timeout=15) + return client + + +client = get_client() + + +def podman(): + binary = os.getenv("PODMAN_BINARY") + if binary is None: + binary = "../../../bin/podman" + return binary + + +def restore_image_from_cache(TestClass): + alpineImage = os.path.join(constant.ImageCacheDir, + alpineDict["tarballName"]) + if not os.path.exists(alpineImage): + os.makedirs(constant.ImageCacheDir, exist_ok=True) + client.pull(constant.ALPINE) + image = client.get_image(constant.ALPINE) + tarball = open(alpineImage, mode="wb") + for frame in image: + tarball.write(frame) + tarball.close() + else: + subprocess.run( + [podman(), "load", "-i", alpineImage], + shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=True, + ) + + +def flush_image_cache(TestCase): + for f in pathlib.Path(constant.ImageCacheDir).glob("*"): + f.unlink(f) + + +def run_top_container(): + c = client.create_container(image=constant.ALPINE, + command='/bin/sleep 5', + name=constant.TOP) + client.start(container=c.get("Id")) + return c.get("Id") + + +def enable_sock(TestClass): + TestClass.podman = subprocess.Popen( + [ + podman(), "system", "service", "tcp:localhost:8080", + "--log-level=debug", "--time=0" + ], + shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + time.sleep(2) + + +def terminate_connection(TestClass): + TestClass.podman.terminate() + stdout, stderr = TestClass.podman.communicate(timeout=0.5) + if stdout: + print("\nService Stdout:\n" + stdout.decode('utf-8')) + if stderr: + print("\nService Stderr:\n" + stderr.decode('utf-8')) + + if TestClass.podman.returncode > 0: + sys.stderr.write("podman exited with error code {}\n".format( + TestClass.podman.returncode)) + sys.exit(2) + + +def remove_all_containers(): + containers = client.containers(quiet=True) + for c in containers: + client.remove_container(container=c.get("Id"), force=True) + + +def remove_all_images(): + allImages = client.images() + for image in allImages: + client.remove_image(image, force=True) diff --git a/test/python/dockerpy/tests/constant.py b/test/python/dockerpy/tests/constant.py new file mode 100644 index 000000000..b44442d02 --- /dev/null +++ b/test/python/dockerpy/tests/constant.py @@ -0,0 +1,13 @@ +BB = "docker.io/library/busybox:latest" +NGINX = "docker.io/library/nginx:latest" +ALPINE = "docker.io/library/alpine:latest" +ALPINE_SHORTNAME = "alpine" +ALPINELISTTAG = "docker.io/library/alpine:3.10.2" +ALPINELISTDIGEST = "docker.io/library/alpine@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb" +ALPINEAMD64DIGEST = "docker.io/library/alpine@sha256:acd3ca9941a85e8ed16515bfc5328e4e2f8c128caa72959a58a127b7801ee01f" +ALPINEAMD64ID = "961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4" +ALPINEARM64DIGEST = "docker.io/library/alpine@sha256:db7f3dcef3d586f7dd123f107c93d7911515a5991c4b9e51fa2a43e46335a43e" +ALPINEARM64ID = "915beeae46751fc564998c79e73a1026542e945ca4f73dc841d09ccc6c2c0672" +infra = "k8s.gcr.io/pause:3.2" +TOP = "top" +ImageCacheDir = "/tmp/podman/imagecachedir" diff --git a/test/test_dockerpy/test_containers.py b/test/python/dockerpy/tests/test_containers.py index 34fe82c18..6b89688d4 100644 --- a/test/test_dockerpy/test_containers.py +++ b/test/python/dockerpy/tests/test_containers.py @@ -1,18 +1,15 @@ - -import unittest -import docker -import requests import os -from docker import Client -from . import constant -from . import common import time +import unittest + +import requests + +from . import common, constant client = common.get_client() -class TestContainers(unittest.TestCase): - podman = None +class TestContainers(unittest.TestCase): topContainerId = "" def setUp(self): @@ -33,6 +30,7 @@ class TestContainers(unittest.TestCase): @classmethod def tearDownClass(cls): common.terminate_connection(cls) + common.flush_image_cache(cls) return super().tearDownClass() def test_inspect_container(self): @@ -42,16 +40,15 @@ class TestContainers(unittest.TestCase): self.assertEqual(error.exception.response.status_code, 404) # Inspect valid container by name container = client.inspect_container(constant.TOP) - self.assertIn(TestContainers.topContainerId , container["Id"]) + self.assertIn(TestContainers.topContainerId, container["Id"]) # Inspect valid container by Id container = client.inspect_container(TestContainers.topContainerId) - self.assertIn(constant.TOP , container["Name"]) + self.assertIn(constant.TOP, container["Name"]) def test_create_container(self): # Run a container with detach mode container = client.create_container(image="alpine", detach=True) - self.assertEqual(len(container),2) - + self.assertEqual(len(container), 2) def test_start_container(self): # Start bogus container @@ -65,9 +62,9 @@ class TestContainers(unittest.TestCase): # self.assertEqual(error.exception.response.status_code, 304) # Create a new container and validate the count - client.create_container(image=constant.ALPINE,name="container2") - containers = client.containers(quiet=True,all=True) - self.assertEqual(len(containers),2) + client.create_container(image=constant.ALPINE, name="container2") + containers = client.containers(quiet=True, all=True) + self.assertEqual(len(containers), 2) def test_stop_container(self): # Stop bogus container @@ -82,7 +79,10 @@ class TestContainers(unittest.TestCase): # Stop a running container and validate the state client.stop(TestContainers.topContainerId) container = client.inspect_container(constant.TOP) - self.assertIn(container["State"]["Status"],"stopped exited",) + self.assertIn( + container["State"]["Status"], + "stopped exited", + ) def test_restart_container(self): # Restart bogus container @@ -109,12 +109,12 @@ class TestContainers(unittest.TestCase): # Remove container by ID with force client.remove_container(TestContainers.topContainerId, force=True) containers = client.containers() - self.assertEqual(len(containers),0) + self.assertEqual(len(containers), 0) def test_remove_container_without_force(self): # Validate current container count containers = client.containers() - self.assertTrue(len(containers),1) + self.assertTrue(len(containers), 1) # Remove running container should throw error with self.assertRaises(requests.HTTPError) as error: @@ -125,7 +125,7 @@ class TestContainers(unittest.TestCase): client.stop(TestContainers.topContainerId) client.remove_container(TestContainers.topContainerId) containers = client.containers() - self.assertEqual(len(containers),0) + self.assertEqual(len(containers), 0) def test_pause_container(self): # Pause bogus container @@ -151,7 +151,6 @@ class TestContainers(unittest.TestCase): client.pause(TestContainers.topContainerId) self.assertEqual(error.exception.response.status_code, 500) - def test_unpause_container(self): # Unpause bogus container with self.assertRaises(requests.HTTPError) as error: @@ -173,7 +172,7 @@ class TestContainers(unittest.TestCase): # Add container and validate the count client.create_container(image="alpine", detach=True) containers = client.containers(all=True) - self.assertEqual(len(containers),2) + self.assertEqual(len(containers), 2) # Not working for now......checking # # List container with filter by id diff --git a/test/test_dockerpy/test_images.py b/test/python/dockerpy/tests/test_images.py index c88353b79..5eae61c2f 100644 --- a/test/test_dockerpy/test_images.py +++ b/test/python/dockerpy/tests/test_images.py @@ -1,17 +1,18 @@ - +import os +import stat import unittest +from os import remove +from stat import ST_SIZE + import docker import requests -import os -from docker import Client -from . import constant -from . import common + +from . import common, constant client = common.get_client() -class TestImages(unittest.TestCase): - podman = None +class TestImages(unittest.TestCase): def setUp(self): super().setUp() common.restore_image_from_cache(self) @@ -25,13 +26,12 @@ class TestImages(unittest.TestCase): super().setUpClass() common.enable_sock(cls) - @classmethod def tearDownClass(cls): common.terminate_connection(cls) + common.flush_image_cache(cls) return super().tearDownClass() - # Inspect Image def test_inspect_image(self): @@ -43,46 +43,46 @@ class TestImages(unittest.TestCase): # Tag Image - # Validates if invalid image name is given a bad response is encountered. +# Validates if invalid image name is given a bad response is encountered. + def test_tag_invalid_image(self): with self.assertRaises(requests.HTTPError): - client.tag("dummy","demo") - - + client.tag("dummy", "demo") # Validates if the image is tagged successfully. def test_tag_valid_image(self): - client.tag(constant.ALPINE,"demo",constant.ALPINE_SHORTNAME) + client.tag(constant.ALPINE, "demo", constant.ALPINE_SHORTNAME) alpine_image = client.inspect_image(constant.ALPINE) for x in alpine_image["RepoTags"]: - if("demo:alpine" in x): + if ("demo:alpine" in x): self.assertTrue self.assertFalse # Validates if name updates when the image is retagged. @unittest.skip("dosent work now") def test_retag_valid_image(self): - client.tag(constant.ALPINE_SHORTNAME, "demo","rename") + client.tag(constant.ALPINE_SHORTNAME, "demo", "rename") alpine_image = client.inspect_image(constant.ALPINE) self.assertNotIn("demo:test", alpine_image["RepoTags"]) # List Image - # List All Images +# List All Images + def test_list_images(self): allImages = client.images() self.assertEqual(len(allImages), 1) # Add more images client.pull(constant.BB) allImages = client.images() - self.assertEqual(len(allImages) , 2) - + self.assertEqual(len(allImages), 2) - # List images with filter - filters = {'reference':'alpine'} - allImages = client.images(filters = filters) - self.assertEqual(len(allImages) , 1) + # List images with filter + filters = {'reference': 'alpine'} + allImages = client.images(filters=filters) + self.assertEqual(len(allImages), 1) # Search Image + def test_search_image(self): response = client.search("alpine") for i in response: @@ -94,22 +94,25 @@ class TestImages(unittest.TestCase): # Image Exist (No docker-py support yet) # Remove Image + def test_remove_image(self): # Check for error with wrong image name with self.assertRaises(requests.HTTPError): client.remove_image("dummy") allImages = client.images() - self.assertEqual(len(allImages) , 1) + self.assertEqual(len(allImages), 1) alpine_image = client.inspect_image(constant.ALPINE) client.remove_image(alpine_image) allImages = client.images() - self.assertEqual(len(allImages) , 0) + self.assertEqual(len(allImages), 0) # Image History + def test_image_history(self): # Check for error with wrong image name with self.assertRaises(requests.HTTPError): - client.remove_image("dummy") + client.history("dummy") + imageHistory = client.history(constant.ALPINE) alpine_image = client.inspect_image(constant.ALPINE) for h in imageHistory: @@ -119,28 +122,37 @@ class TestImages(unittest.TestCase): # Prune Image (No docker-py support yet) + def test_get_image_dummy(self): + # FIXME: seems to be an error in the library + self.skipTest("Documentation and library do not match") + # Check for error with wrong image name + with self.assertRaises(docker.errors.ImageNotFound): + client.get_image("dummy") + # Export Image def test_export_image(self): client.pull(constant.BB) - file = os.path.join(constant.ImageCacheDir , "busybox.tar") if not os.path.exists(constant.ImageCacheDir): os.makedirs(constant.ImageCacheDir) - # Check for error with wrong image name - with self.assertRaises(requests.HTTPError): - client.get_image("dummy") - response = client.get_image(constant.BB) - image_tar = open(file,mode="wb") - image_tar.write(response.data) - image_tar.close() - os.stat(file) + + image = client.get_image(constant.BB) + + file = os.path.join(constant.ImageCacheDir, "busybox.tar") + tarball = open(file, mode="wb") + for frame in image: + tarball.write(frame) + tarball.close() + sz = os.path.getsize(file) + self.assertGreater(sz, 0) + # Import|Load Image def test_import_image(self): allImages = client.images() self.assertEqual(len(allImages), 1) - file = os.path.join(constant.ImageCacheDir , "busybox.tar") + file = os.path.join(constant.ImageCacheDir, "alpine.tar") client.import_image_from_file(filename=file) allImages = client.images() self.assertEqual(len(allImages), 2) diff --git a/test/test_dockerpy/test_info_version.py b/test/python/dockerpy/tests/test_info_version.py index be1a2aab9..e3ee18ec7 100644 --- a/test/test_dockerpy/test_info_version.py +++ b/test/python/dockerpy/tests/test_info_version.py @@ -1,11 +1,10 @@ import unittest -import docker -from docker import Client -from . import constant -from . import common + +from . import common, constant client = common.get_client() + class TestInfo_Version(unittest.TestCase): podman = None @@ -31,16 +30,15 @@ class TestInfo_Version(unittest.TestCase): common.terminate_connection(cls) return super().tearDownClass() - def test_Info(self): self.assertIsNotNone(client.info()) def test_info_container_details(self): info = client.info() - self.assertEqual(info["Containers"],1) + self.assertEqual(info["Containers"], 1) client.create_container(image=constant.ALPINE) info = client.info() - self.assertEqual(info["Containers"],2) + self.assertEqual(info["Containers"], 2) def test_version(self): self.assertIsNotNone(client.version()) diff --git a/test/system/015-help.bats b/test/system/015-help.bats index 14af8e1a4..3d05b44fe 100644 --- a/test/system/015-help.bats +++ b/test/system/015-help.bats @@ -34,13 +34,16 @@ function check_help() { dprint "$command_string --help" run_podman "$@" $cmd --help + local full_help="$output" # The line immediately after 'Usage:' gives us a 1-line synopsis - usage=$(echo "$output" | grep -A1 '^Usage:' | tail -1) + usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1) [ -n "$usage" ] || die "podman $cmd: no Usage message found" # e.g. 'podman ps' should not show 'podman container ps' in usage - is "$usage" " $command_string .*" "Usage string matches command" + # Trailing space in usage handles 'podman system renumber' which + # has no ' [flags]' + is "$usage " " $command_string .*" "Usage string matches command" # If usage ends in '[command]', recurse into subcommands if expr "$usage" : '.*\[command\]$' >/dev/null; then @@ -59,6 +62,17 @@ function check_help() { die "'flags' must precede arguments in usage: $usage" fi + # Cross-check: if usage includes '[flags]', there must be a + # longer 'Flags:' section in the full --help output; vice-versa, + # if 'Flags:' is in full output, usage line must have '[flags]'. + if expr "$usage" : '.*\[flag' >/dev/null; then + if ! expr "$full_help" : ".*Flags:" >/dev/null; then + die "$command_string: Usage includes '[flags]' but has no 'Flags:' subsection" + fi + elif expr "$full_help" : ".*Flags:" >/dev/null; then + die "$command_string: --help has 'Flags:' section but no '[flags]' in synopsis" + fi + # If usage lists no arguments (strings in ALL CAPS), confirm # by running with 'invalid-arg' and expecting failure. if ! expr "$usage" : '.*[A-Z]' >/dev/null; then diff --git a/test/system/030-run.bats b/test/system/030-run.bats index eeecea2e5..bc6347012 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -201,4 +201,45 @@ echo $rand | 0 | $rand "podman will not overwrite existing cidfile" } +@test "podman run docker-archive" { + # Create an image that, when run, outputs a random magic string + expect=$(random_string 20) + run_podman run --name myc --entrypoint="[\"/bin/echo\",\"$expect\"]" $IMAGE + is "$output" "$expect" "podman run --entrypoint echo-randomstring" + + # Save it as a tar archive + run_podman commit myc myi + archive=$PODMAN_TMPDIR/archive.tar + run_podman save myi -o $archive + is "$output" "" "podman save" + + # Clean up image and container from container storage... + run_podman rmi myi + run_podman rm myc + + # ... then confirm we can run from archive. This re-imports the image + # and runs it, producing our random string as the last line. + run_podman run docker-archive:$archive + is "${lines[0]}" "Getting image source signatures" "podman run docker-archive, first line of output" + is "$output" ".*Copying blob" "podman run docker-archive" + is "$output" ".*Copying config" "podman run docker-archive" + is "$output" ".*Writing manifest" "podman run docker-archive" + is "${lines[-1]}" "$expect" "podman run docker-archive: expected random string output" + + # Clean up container as well as re-imported image + run_podman rm -a + run_podman rmi myi + + # Repeat the above, with podman-create and podman-start. + run_podman create docker-archive:$archive + cid=${lines[-1]} + + run_podman start --attach $cid + is "$output" "$expect" "'podman run' of 'podman-create docker-archive'" + + # Clean up. + run_podman rm $cid + run_podman rmi myi +} + # vim: filetype=sh diff --git a/test/test_dockerpy/README.md b/test/test_dockerpy/README.md deleted file mode 100644 index 32e426d58..000000000 --- a/test/test_dockerpy/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Dockerpy regression test - -Python test suite to validate Podman endpoints using dockerpy library - -Running tests -============= -To run the tests locally in your sandbox: - -#### Run the entire test - -``` -sudo PYTHONPATH=/usr/bin/python python -m dockerpy.images -``` - -Passing the -v option to your test script will instruct unittest.main() to enable a higher level of verbosity, and produce detailed output: - -``` -sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images -``` -#### Run a specific test class - -``` -sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images.TestImages -``` - -#### Run a specific test within the test class - -``` -sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images.TestImages.test_list_images -``` diff --git a/test/test_dockerpy/common.py b/test/test_dockerpy/common.py deleted file mode 100644 index 975b13dc6..000000000 --- a/test/test_dockerpy/common.py +++ /dev/null @@ -1,85 +0,0 @@ -import docker -import subprocess -import os -import sys -import time -from docker import Client -from . import constant - -alpineDict = { - "name": "docker.io/library/alpine:latest", - "shortName": "alpine", - "tarballName": "alpine.tar"} - -def get_client(): - client = docker.Client(base_url="http://localhost:8080",timeout=15) - return client - -client = get_client() - -def podman(): - binary = os.getenv("PODMAN_BINARY") - if binary is None: - binary = "bin/podman" - return binary - -def restore_image_from_cache(TestClass): - alpineImage = os.path.join(constant.ImageCacheDir , alpineDict["tarballName"]) - if not os.path.exists(alpineImage): - os.makedirs(constant.ImageCacheDir) - client.pull(constant.ALPINE) - response = client.get_image(constant.ALPINE) - image_tar = open(alpineImage,mode="wb") - image_tar.write(response.data) - image_tar.close() - else : - TestClass.podman = subprocess.run( - [ - podman(), "load", "-i", alpineImage - ], - shell=False, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - -def run_top_container(): - c = client.create_container(image=constant.ALPINE,command='/bin/sleep 5',name=constant.TOP) - client.start(container=c.get("Id")) - return c.get("Id") - -def enable_sock(TestClass): - TestClass.podman = subprocess.Popen( - [ - podman(), "system", "service", "tcp:localhost:8080", - "--log-level=debug", "--time=0" - ], - shell=False, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - time.sleep(2) - -def terminate_connection(TestClass): - TestClass.podman.terminate() - stdout, stderr = TestClass.podman.communicate(timeout=0.5) - if stdout: - print("\nService Stdout:\n" + stdout.decode('utf-8')) - if stderr: - print("\nService Stderr:\n" + stderr.decode('utf-8')) - - if TestClass.podman.returncode > 0: - sys.stderr.write("podman exited with error code {}\n".format( - TestClass.podman.returncode)) - sys.exit(2) - -def remove_all_containers(): - containers = client.containers(quiet=True) - for c in containers: - client.remove_container(container=c.get("Id"), force=True) - -def remove_all_images(): - allImages = client.images() - for image in allImages: - client.remove_image(image,force=True) diff --git a/test/test_dockerpy/constant.py b/test/test_dockerpy/constant.py deleted file mode 100644 index 8a3f1d984..000000000 --- a/test/test_dockerpy/constant.py +++ /dev/null @@ -1,13 +0,0 @@ -BB = "docker.io/library/busybox:latest" -NGINX = "docker.io/library/nginx:latest" -ALPINE = "docker.io/library/alpine:latest" -ALPINE_SHORTNAME = "alpine" -ALPINELISTTAG = "docker.io/library/alpine:3.10.2" -ALPINELISTDIGEST = "docker.io/library/alpine@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb" -ALPINEAMD64DIGEST = "docker.io/library/alpine@sha256:acd3ca9941a85e8ed16515bfc5328e4e2f8c128caa72959a58a127b7801ee01f" -ALPINEAMD64ID = "961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4" -ALPINEARM64DIGEST = "docker.io/library/alpine@sha256:db7f3dcef3d586f7dd123f107c93d7911515a5991c4b9e51fa2a43e46335a43e" -ALPINEARM64ID = "915beeae46751fc564998c79e73a1026542e945ca4f73dc841d09ccc6c2c0672" -infra = "k8s.gcr.io/pause:3.2" -TOP = "top" -ImageCacheDir = "/tmp/podman/imagecachedir" diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor.go index 8b4207efc..8046f45f5 100644 --- a/vendor/github.com/containers/common/pkg/apparmor/apparmor.go +++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor.go @@ -2,14 +2,16 @@ package apparmor import ( "errors" + + "github.com/containers/common/version" ) const ( // ProfilePrefix is used for version-independent presence checks. - ProfilePrefix = "apparmor_profile" + ProfilePrefix = "containers-default-" // Profile default name - Profile = "container-default" + Profile = ProfilePrefix + version.Version ) var ( diff --git a/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go index f0fab4597..307249f3d 100644 --- a/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go +++ b/vendor/github.com/containers/common/pkg/apparmor/apparmor_linux.go @@ -255,9 +255,11 @@ func CheckProfileAndLoadDefault(name string) (string, error) { } } - // If the specified name is not empty or is not a default libpod one, - // ignore it and return the name. - if name != "" && !strings.HasPrefix(name, ProfilePrefix) { + if name == "" { + name = Profile + } else if !strings.HasPrefix(name, ProfilePrefix) { + // If the specified name is not a default one, ignore it and return the + // name. isLoaded, err := IsLoaded(name) if err != nil { return "", err @@ -268,7 +270,6 @@ func CheckProfileAndLoadDefault(name string) (string, error) { return name, nil } - name = Profile // To avoid expensive redundant loads on each invocation, check // if it's loaded before installing it. isLoaded, err := IsLoaded(name) diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index ce479088e..d60464739 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -822,6 +822,7 @@ func stringsEq(a, b []string) bool { var ( configOnce sync.Once + configErr error config *Config ) @@ -837,11 +838,10 @@ var ( // The system defaults container config files can be overwritten using the // CONTAINERS_CONF environment variable. This is usually done for testing. func Default() (*Config, error) { - var err error configOnce.Do(func() { - config, err = NewConfig("") + config, configErr = NewConfig("") }) - return config, err + return config, configErr } func Path() string { diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go new file mode 100644 index 000000000..38a45dfb2 --- /dev/null +++ b/vendor/github.com/containers/common/version/version.go @@ -0,0 +1,4 @@ +package version + +// Version is the version of the build. +const Version = "0.14.3" diff --git a/vendor/modules.txt b/vendor/modules.txt index 882ea5d25..497381b52 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,13 +84,14 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.14.0 +# github.com/containers/common v0.14.3 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/auth github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/config github.com/containers/common/pkg/sysinfo +github.com/containers/common/version # github.com/containers/conmon v2.0.18+incompatible github.com/containers/conmon/runner/config # github.com/containers/image/v5 v5.5.1 |