diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | cmd/podman/push.go | 5 | ||||
-rw-r--r-- | contrib/systemd/system/podman-docker.conf | 1 | ||||
-rw-r--r-- | docs/source/markdown/podman-container-exists.1.md | 4 | ||||
-rw-r--r-- | docs/source/markdown/podman-history.1.md | 12 | ||||
-rw-r--r-- | docs/source/markdown/podman-image-exists.1.md | 4 | ||||
-rw-r--r-- | docs/source/markdown/podman-inspect.1.md | 2 | ||||
-rw-r--r-- | docs/source/markdown/podman-rm.1.md | 22 | ||||
-rw-r--r-- | docs/source/markdown/podman-rmi.1.md | 8 | ||||
-rw-r--r-- | docs/source/markdown/podman-system-prune.1.md | 6 | ||||
-rw-r--r-- | install.md | 12 | ||||
-rw-r--r-- | libpod/image/image.go | 9 | ||||
-rw-r--r-- | pkg/inspect/inspect.go | 46 | ||||
-rw-r--r-- | test/apiv2/00-TEMPLATE | 6 | ||||
-rw-r--r-- | test/apiv2/01-basic.at | 49 | ||||
-rw-r--r-- | test/apiv2/10-images.at | 36 | ||||
-rw-r--r-- | test/apiv2/20-containers.at | 29 | ||||
-rw-r--r-- | test/apiv2/30-volumes.at | 14 | ||||
-rw-r--r-- | test/apiv2/40-pods.at | 33 | ||||
-rw-r--r-- | test/apiv2/README.md | 63 | ||||
-rwxr-xr-x | test/apiv2/test-apiv2 | 325 | ||||
-rw-r--r-- | test/e2e/inspect_test.go | 13 |
22 files changed, 648 insertions, 53 deletions
@@ -452,6 +452,8 @@ install.docker: docker-docs install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 install ${SELINUXOPT} -m 755 docker $(DESTDIR)$(BINDIR)/docker install ${SELINUXOPT} -m 644 docs/build/man/docker*.1 -t $(DESTDIR)$(MANDIR)/man1 + install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} ${DESTDIR}${TMPFILESDIR} + install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-docker.conf -t ${DESTDIR}${TMPFILESDIR} install.systemd: install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} ${DESTDIR}${TMPFILESDIR} diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 1be8dfe11..b078959ba 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -100,7 +100,8 @@ func pushCmd(c *cliconfig.PushValues) error { // --compress and --format can only be used for the "dir" transport splitArg := strings.SplitN(destName, ":", 2) - if c.Flag("compress").Changed || c.Flag("format").Changed { + + if c.IsSet("compress") || c.Flag("format").Changed { if splitArg[0] != directory.Transport.Name() { return errors.Errorf("--compress and --format can be set only when pushing to a directory using the 'dir' transport") } @@ -141,7 +142,7 @@ func pushCmd(c *cliconfig.PushValues) error { DockerRegistryCreds: registryCreds, DockerCertPath: certPath, } - if c.Flag("tls-verify").Changed { + if c.IsSet("tls-verify") { dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } diff --git a/contrib/systemd/system/podman-docker.conf b/contrib/systemd/system/podman-docker.conf new file mode 100644 index 000000000..e12f19bce --- /dev/null +++ b/contrib/systemd/system/podman-docker.conf @@ -0,0 +1 @@ +L+ /run/docker.sock - - - - /run/podman/podman.sock diff --git a/docs/source/markdown/podman-container-exists.1.md b/docs/source/markdown/podman-container-exists.1.md index 4d988132b..3b4ca33e4 100644 --- a/docs/source/markdown/podman-container-exists.1.md +++ b/docs/source/markdown/podman-container-exists.1.md @@ -21,7 +21,7 @@ Print usage statement Check if an container called `webclient` exists in local storage (the container does actually exist). ``` -$ sudo podman container exists webclient +$ podman container exists webclient $ echo $? 0 $ @@ -29,7 +29,7 @@ $ Check if an container called `webbackend` exists in local storage (the container does not actually exist). ``` -$ sudo podman container exists webbackend +$ podman container exists webbackend $ echo $? 1 $ diff --git a/docs/source/markdown/podman-history.1.md b/docs/source/markdown/podman-history.1.md index 078864faa..1a8f8906c 100644 --- a/docs/source/markdown/podman-history.1.md +++ b/docs/source/markdown/podman-history.1.md @@ -29,17 +29,17 @@ Valid placeholders for the Go template are listed below: ## OPTIONS -**--human**, **-H** +**--human**, **-H**=*true|false* -Display sizes and dates in human readable format +Display sizes and dates in human readable format (default *true*). -**--no-trunc** +**--no-trunc**=*true|false* -Do not truncate the output +Do not truncate the output (default *false*). -**--quiet**, **-q** +**--quiet**, **-q**=*true|false* -Print the numeric IDs only +Print the numeric IDs only (default *false*). **--format**=*format* diff --git a/docs/source/markdown/podman-image-exists.1.md b/docs/source/markdown/podman-image-exists.1.md index f6a89e2aa..3b7127b64 100644 --- a/docs/source/markdown/podman-image-exists.1.md +++ b/docs/source/markdown/podman-image-exists.1.md @@ -22,7 +22,7 @@ Print usage statement Check if an image called `webclient` exists in local storage (the image does actually exist). ``` -$ sudo podman image exists webclient +$ podman image exists webclient $ echo $? 0 $ @@ -30,7 +30,7 @@ $ Check if an image called `webbackend` exists in local storage (the image does not actually exist). ``` -$ sudo podman image exists webbackend +$ podman image exists webbackend $ echo $? 1 $ diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md index ad4d0659e..4998772c3 100644 --- a/docs/source/markdown/podman-inspect.1.md +++ b/docs/source/markdown/podman-inspect.1.md @@ -36,7 +36,7 @@ The latest option is not supported on the remote client or when invoked as *podm **--size**, **-s** -Display the total file size if the type is a container +In addition to normal output, display the total file size if the type is a container. ## EXAMPLE diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md index f6dac8db4..cddf06e3e 100644 --- a/docs/source/markdown/podman-rm.1.md +++ b/docs/source/markdown/podman-rm.1.md @@ -10,13 +10,13 @@ podman\-rm - Remove one or more containers ## DESCRIPTION **podman rm** will remove one or more containers from the host. The container name or ID can be used. This does not remove images. -Running or unusable containers will not be removed without the `-f` option. +Running or unusable containers will not be removed without the **-f** option. ## OPTIONS **--all**, **-a** -Remove all containers. Can be used in conjunction with -f as well. +Remove all containers. Can be used in conjunction with **-f** as well. **--cidfile** @@ -46,44 +46,44 @@ The latest option is not supported on the remote client. **--storage** Remove the container from the storage library only. -This is only possible with containers that are not present in libpod (cannot be seen by `podman ps`). -It is used to remove containers from `podman build` and `buildah`, and orphan containers which were only partially removed by `podman rm`. +This is only possible with containers that are not present in libpod (cannot be seen by **podman ps**). +It is used to remove containers from **podman build** and **buildah**, and orphan containers which were only partially removed by **podman rm**. The storage option conflicts with the **--all**, **--latest**, and **--volumes** options. **--volumes**, **-v** Remove anonymous volumes associated with the container. This does not include named volumes -created with `podman volume create`, or the `--volume` option of `podman run` and `podman create`. +created with **podman volume create**, or the **--volume** option of **podman run** and **podman create**. ## EXAMPLE Remove a container by its name *mywebserver* ``` -podman rm mywebserver +$ podman rm mywebserver ``` Remove several containers by name and container id. ``` -podman rm mywebserver myflaskserver 860a4b23 +$ podman rm mywebserver myflaskserver 860a4b23 ``` Remove several containers reading their IDs from files. ``` -podman rm --cidfile ./cidfile-1 --cidfile /home/user/cidfile-2 +$ podman rm --cidfile ./cidfile-1 --cidfile /home/user/cidfile-2 ``` Forcibly remove a container by container ID. ``` -podman rm -f 860a4b23 +$ podman rm -f 860a4b23 ``` Remove all containers regardless of its run state. ``` -podman rm -f -a +$ podman rm -f -a ``` Forcibly remove the latest container created. ``` -podman rm -f --latest +$ podman rm -f --latest ``` ## Exit Status diff --git a/docs/source/markdown/podman-rmi.1.md b/docs/source/markdown/podman-rmi.1.md index 0f5af3a45..78ef2b157 100644 --- a/docs/source/markdown/podman-rmi.1.md +++ b/docs/source/markdown/podman-rmi.1.md @@ -24,21 +24,21 @@ This option will cause podman to remove all containers that are using the image Remove an image by its short ID ``` -podman rmi c0ed59d05ff7 +$ podman rmi c0ed59d05ff7 ``` Remove an image and its associated containers. ``` -podman rmi --force imageID +$ podman rmi --force imageID ``` Remove multiple images by their shortened IDs. ``` -podman rmi c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7 +$ podman rmi c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7 ``` Remove all images and containers. ``` -podman rmi -a -f +$ podman rmi -a -f ``` ## Exit Status **0** All specified images removed diff --git a/docs/source/markdown/podman-system-prune.1.md b/docs/source/markdown/podman-system-prune.1.md index e6297dc0b..9c6ef5d8b 100644 --- a/docs/source/markdown/podman-system-prune.1.md +++ b/docs/source/markdown/podman-system-prune.1.md @@ -9,9 +9,9 @@ podman\-system\-prune - Remove all unused container, image and volume data ## DESCRIPTION **podman system prune** removes all unused containers (both dangling and unreferenced), pods and optionally, volumes from local storage. -With the `all` option, you can delete all unused images. Unused images are dangling images as well as any image that does not have any containers based on it. +With the **--all** option, you can delete all unused images. Unused images are dangling images as well as any image that does not have any containers based on it. -By default, volumes are not removed to prevent important data from being deleted if there is currently no container using the volume. Use the --volumes flag when running the command to prune volumes as well. +By default, volumes are not removed to prevent important data from being deleted if there is currently no container using the volume. Use the **--volumes** flag when running the command to prune volumes as well. ## OPTIONS **--all**, **-a** @@ -28,7 +28,7 @@ Print usage statement **--volumes** -Prune volumes not used by at least one container +Prune volumes currently unused by any container ## SEE ALSO podman(1), podman-image-prune(1), podman-container-prune(1), podman-pod-prune(1), podman-volume-prune(1) diff --git a/install.md b/install.md index 90ad4f233..05cbce7c9 100644 --- a/install.md +++ b/install.md @@ -68,6 +68,16 @@ Using [Homebrew](https://brew.sh/): brew cask install podman ``` +#### [OpenEmbedded](https://www.openembedded.org) + +Bitbake recipes for podman and its dependencies are available in the +[meta-virtualization layer](https://git.yoctoproject.org/cgit/cgit.cgi/meta-virtualization/). +Add the layer to your OpenEmbedded build environment and build podman using: + +```bash +bitbake podman +``` + #### [openSUSE](https://www.opensuse.org) ```bash @@ -177,6 +187,8 @@ If you use a newer Podman package from Fedora's `updates-testing`, we would appreciate your `+1` feedback in [Bodhi, Fedora's update management system](https://bodhi.fedoraproject.org/updates/?packages=podman). +If you are running a non-rawhide Fedora distribution, you can also test the latest packages +with our [COPR repository](https://copr.fedorainfracloud.org/coprs/baude/Upstream_CRIO_Family/). #### [Raspbian](https://raspbian.org) diff --git a/libpod/image/image.go b/libpod/image/image.go index 6ea49e2a9..7ee8c45d7 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -1012,6 +1012,15 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { History: ociv1Img.History, NamesHistory: i.NamesHistory(), } + if manifestType == manifest.DockerV2Schema2MediaType { + hc, err := i.GetHealthCheck(ctx) + if err != nil { + return nil, err + } + if hc != nil { + data.HealthCheck = hc + } + } return data, nil } diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go index 8249dc4aa..569f208d9 100644 --- a/pkg/inspect/inspect.go +++ b/pkg/inspect/inspect.go @@ -3,6 +3,7 @@ package inspect import ( "time" + "github.com/containers/image/v5/manifest" "github.com/containers/libpod/libpod/driver" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" @@ -10,28 +11,29 @@ import ( // ImageData holds the inspect information of an image type ImageData struct { - ID string `json:"Id"` - Digest digest.Digest `json:"Digest"` - RepoTags []string `json:"RepoTags"` - RepoDigests []string `json:"RepoDigests"` - Parent string `json:"Parent"` - Comment string `json:"Comment"` - Created *time.Time `json:"Created"` - Config *v1.ImageConfig `json:"Config"` - Version string `json:"Version"` - Author string `json:"Author"` - Architecture string `json:"Architecture"` - Os string `json:"Os"` - Size int64 `json:"Size"` - VirtualSize int64 `json:"VirtualSize"` - GraphDriver *driver.Data `json:"GraphDriver"` - RootFS *RootFS `json:"RootFS"` - Labels map[string]string `json:"Labels"` - Annotations map[string]string `json:"Annotations"` - ManifestType string `json:"ManifestType"` - User string `json:"User"` - History []v1.History `json:"History"` - NamesHistory []string `json:"NamesHistory"` + ID string `json:"Id"` + Digest digest.Digest `json:"Digest"` + RepoTags []string `json:"RepoTags"` + RepoDigests []string `json:"RepoDigests"` + Parent string `json:"Parent"` + Comment string `json:"Comment"` + Created *time.Time `json:"Created"` + Config *v1.ImageConfig `json:"Config"` + Version string `json:"Version"` + Author string `json:"Author"` + Architecture string `json:"Architecture"` + Os string `json:"Os"` + Size int64 `json:"Size"` + VirtualSize int64 `json:"VirtualSize"` + GraphDriver *driver.Data `json:"GraphDriver"` + RootFS *RootFS `json:"RootFS"` + Labels map[string]string `json:"Labels"` + Annotations map[string]string `json:"Annotations"` + ManifestType string `json:"ManifestType"` + User string `json:"User"` + History []v1.History `json:"History"` + NamesHistory []string `json:"NamesHistory"` + HealthCheck *manifest.Schema2HealthConfig `json:"Healthcheck,omitempty"` } // RootFS holds the root fs information of an image diff --git a/test/apiv2/00-TEMPLATE b/test/apiv2/00-TEMPLATE new file mode 100644 index 000000000..e256371ca --- /dev/null +++ b/test/apiv2/00-TEMPLATE @@ -0,0 +1,6 @@ +# -*- sh -*- +# +# FIXME: one-line description of the purpose of this file +# + +# vim: filetype=sh diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at new file mode 100644 index 000000000..e87ec534c --- /dev/null +++ b/test/apiv2/01-basic.at @@ -0,0 +1,49 @@ +# -*- sh -*- +# +# The earliest most basic tests. If any of these fail, life is bad +# + +# NOTE: paths with a leading slash will be interpreted as-is; +# paths without will have '/v1.40/' prepended. +t GET /_ping 200 OK +t HEAD /_ping 200 +t GET /libpod/_ping 200 OK + +for i in /version version; do + t GET $i 200 \ + .Components[0].Name="Podman Engine" \ + .Components[0].Details.APIVersion=1.40 \ + .Components[0].Details.MinAPIVersion=1.24 \ + .Components[0].Details.Os=linux \ + .ApiVersion=1.40 \ + .MinAPIVersion=1.24 \ + .Os=linux +done + +# +# Garbage tests - requests that should yield errors +# +t GET /nonesuch 404 +t POST /nonesuch '' 404 +t GET container/nonesuch/json 404 +t GET libpod/containers/nonesuch/json 404 +t GET 'libpod/containers/json?a=b' 400 + +# Method not allowed +t POST /_ping '' 405 +t DELETE /_ping 405 +t POST libpod/containers/json '' 405 +t POST libpod/pods/abc '' 405 +t POST info '' 405 +t GET libpod/containers/create 405 + +# +# system info +# +# FIXME: run 'podman info --format=json', and compare select fields +t GET info 200 \ + .OSType=linux \ + .DefaultRuntime=runc \ + .MemTotal~[0-9]\\+ + +# vim: filetype=sh diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at new file mode 100644 index 000000000..243b35e9f --- /dev/null +++ b/test/apiv2/10-images.at @@ -0,0 +1,36 @@ +# -*- sh -*- +# +# Tests for image-related endpoints +# + +# FIXME: API doesn't support pull yet, so use podman +podman pull -q $IMAGE + +# We want the SHA without the "sha256:" prefix +full_iid=$(podman images --no-trunc --format '{{.ID}}' $IMAGE) +iid=${full_iid##sha256:} + +t GET libpod/images/$iid/exists 204 +t GET libpod/images/$PODMAN_TEST_IMAGE_NAME/exists 204 + +# FIXME: compare to actual podman info +t GET libpod/images/json 200 \ + .[0].Id=${iid} + +t GET libpod/images/$iid/json 200 \ + .Id=$iid \ + .RepoTags[0]=$IMAGE + +# Same thing, but with abbreviated image id +t GET libpod/images/${iid:0:12}/json 200 \ + .Id=$iid \ + .RepoTags[0]=$IMAGE + +# FIXME: docker API incompatibility: libpod returns 'id', docker 'sha256:id' +t GET images/$iid/json 200 \ + .Id=sha256:$iid \ + .RepoTags[0]=$IMAGE + +#t POST images/create fromImage=alpine 201 foo + +# vim: filetype=sh diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at new file mode 100644 index 000000000..5f0a145f0 --- /dev/null +++ b/test/apiv2/20-containers.at @@ -0,0 +1,29 @@ +# -*- sh -*- +# +# test container-related endpoints +# + +podman pull $IMAGE &>/dev/null + +# Unimplemented +#t POST libpod/containers/create '' 201 'sdf' + +# Ensure clean slate +podman rm -a -f &>/dev/null + +t GET libpod/containers/json 200 [] + +podman run $IMAGE true + +t GET libpod/containers/json 200 \ + .[0].ID~[0-9a-f]\\{12\\} \ + .[0].Image=$IMAGE \ + .[0].Command=true \ + .[0].State=4 \ + .[0].IsInfra=false + +cid=$(jq -r '.[0].ID' <<<"$output") + +t DELETE libpod/containers/$cid 204 + +# vim: filetype=sh diff --git a/test/apiv2/30-volumes.at b/test/apiv2/30-volumes.at new file mode 100644 index 000000000..b599680e3 --- /dev/null +++ b/test/apiv2/30-volumes.at @@ -0,0 +1,14 @@ +# -*- sh -*- +# +# volume-related tests +# + +# +# FIXME: endpoints seem to be unimplemented, return 404 +# +if false; then +t GET libpod/volumes/json 200 null +t POST libpod/volumes/create name=foo 201 +fi + +# vim: filetype=sh diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at new file mode 100644 index 000000000..1c25a3822 --- /dev/null +++ b/test/apiv2/40-pods.at @@ -0,0 +1,33 @@ +# -*- sh -*- +# +# test pod-related endpoints +# + +t GET libpod/pods/json 200 null +t POST libpod/pods/create name=foo 201 '{"id":"machine.slice"}' # FIXME! +t GET libpod/pods/foo/exists 204 +t GET libpod/pods/notfoo/exists 404 +t GET libpod/pods/foo/json 200 .Config.name=foo .Containers=null +t GET libpod/pods/json 200 .[0].Config.name=foo .[0].Containers=null + +# Cannot create a dup pod with the same name (FIXME: should that be 409?) +t POST libpod/pods/create name=foo 500 .cause="pod already exists" + +#t POST libpod/pods/create a=b 400 .cause='bad parameter' # FIXME: unimplemented + +t POST libpod/pods/foo/pause '' 204 +t POST libpod/pods/foo/unpause '' 200 +t POST libpod/pods/foo/unpause '' 200 # (2nd time) +t POST libpod/pods/foo/stop '' 304 +t POST libpod/pods/foo/restart '' 500 .cause="no such container" + +t POST libpod/pods/bar/restart '' 404 + +#t POST libpod/pods/prune '' 200 # FIXME: unimplemented, returns 500 +#t POST libpod/pods/prune 'a=b' 400 # FIXME: unimplemented, returns 500 + +# Clean up; and try twice, making sure that the second time fails +t DELETE libpod/pods/foo 204 +t DELETE libpod/pods/foo 404 + +# vim: filetype=sh diff --git a/test/apiv2/README.md b/test/apiv2/README.md new file mode 100644 index 000000000..252d6454e --- /dev/null +++ b/test/apiv2/README.md @@ -0,0 +1,63 @@ +API v2 tests +============ + +This directory contains tests for the podman version 2 API (HTTP). + +Tests themselves are in files of the form 'NN-NAME.at' where NN is a +two-digit number, NAME is a descriptive name, and '.at' is just +an extension I picked. + +Running Tests +============= + +The main test runner is `test-apiv2`. Usage is: + + $ sudo ./test-apiv2 [NAME [...]] + +...where NAME is one or more optional test names, e.g. 'image' or 'pod' +or both. By default, `test-apiv2` will invoke all `*.at` tests. + +`test-apiv2` connects to *localhost only* and *via TCP*. There is +no support here for remote hosts or for UNIX sockets. This is a +framework for testing the API, not all possible protocols. + +`test-apiv2` will start the service if it isn't already running. + + +Writing Tests +============= + +The main test function is `t`. It runs `curl` against the server, +with POST parameters if present, and compares return status and +(optionally) string results from the server: + + t GET /_ping 200 OK + ^^^ ^^^^^^ ^^^ ^^ + | | | +--- expected string result + | | +------- expected return code + | +-------------- endpoint to access + +------------------ method (GET, POST, DELETE, HEAD) + + + t POST libpod/volumes/create name=foo 201 .ID~[0-9a-f]\\{12\\} + ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^ + | | | JSON '.ID': expect 12-char hex + | | +-- expected code + | +----------- POST params + +--------------------------------- note the missing slash + +Notes: + +* If the endpoint has a leading slash (`/_ping`), `t` leaves it unchanged. +If there's no leading slash, `t` prepends `/v1.40`. This is a simple +convenience for simplicity of writing tests. + +* When method is POST, the argument after the endpoint must be a series +of POST arguments in the form 'key=value', separated by commas. `t` will +convert those to JSON form for passing to the server. + +* The final arguments are one or more expected string results. If an +argument starts with a dot, `t` will invoke `jq` on the output to +fetch that field, and will compare it to the right-hand side of +the argument. If the separator is `=` (equals), `t` will require +an exact match; if `~` (tilde), `t` will use `expr` to compare. diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 new file mode 100755 index 000000000..786c976d6 --- /dev/null +++ b/test/apiv2/test-apiv2 @@ -0,0 +1,325 @@ +#!/bin/bash +# +# Usage: test-apiv2 [PORT] +# +# DEVELOPER NOTE: you almost certainly don't need to play in here. See README. +# +ME=$(basename $0) + +############################################################################### +# BEGIN stuff you can but probably shouldn't customize + +PODMAN_TEST_IMAGE_REGISTRY=${PODMAN_TEST_IMAGE_REGISTRY:-"quay.io"} +PODMAN_TEST_IMAGE_USER=${PODMAN_TEST_IMAGE_USER:-"libpod"} +PODMAN_TEST_IMAGE_NAME=${PODMAN_TEST_IMAGE_NAME:-"alpine_labels"} +PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"latest"} +PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG" + +IMAGE=$PODMAN_TEST_IMAGE_FQN + +# END stuff you can but probably shouldn't customize +############################################################################### +# BEGIN setup + +TMPDIR=${TMPDIR:-/tmp} +WORKDIR=$(mktemp --tmpdir -d $ME.tmp.XXXXXX) + +# Log of all HTTP requests and responses +LOG=${TMPDIR}/$ME.log.$(date +'%Y%m%dT%H%M%S') + +HOST=localhost +PORT=${PODMAN_SERVICE_PORT:-8081} + +# Keep track of test count and failures in files, not variables, because +# variables don't carry back up from subshells. +testcounter_file=$WORKDIR/.testcounter +failures_file=$WORKDIR/.failures + +echo 0 >$testcounter_file +echo 0 >$failures_file + +# Where the tests live +TESTS_DIR=$(realpath $(dirname $0)) + +# END setup +############################################################################### +# BEGIN infrastructure code - the helper functions used in tests themselves + +######### +# die # Exit error with a message to stderr +######### +function die() { + echo "$ME: $*" >&2 + exit 1 +} + +######## +# is # Simple comparison +######## +function is() { + local actual=$1 + local expect=$2 + local testname=$3 + + if [ "$actual" = "$expect" ]; then + # On success, include expected value; this helps readers understand + _show_ok 1 "$testname=$expect" + return + fi + _show_ok 0 "$testname" "$expect" "$actual" +} + +########## +# like # Compare, but allowing patterns +########## +function like() { + local actual=$1 + local expect=$2 + local testname=$3 + + if expr "$actual" : "$expect" &>/dev/null; then + # On success, include expected value; this helps readers understand + _show_ok 1 "$testname~$expect" + return + fi + _show_ok 0 "$testname" "~ $expect" "$actual" +} + +############## +# _show_ok # Helper for is() and like(): displays 'ok' or 'not ok' +############## +function _show_ok() { + local ok=$1 + local testname=$2 + + # If output is a tty, colorize pass/fail + local red= + local green= + local reset= + local bold= + if [ -t 3 ]; then + red='\e[31m' + green='\e[32m' + reset='\e[0m' + bold='\e[1m' + fi + + _bump $testcounter_file + count=$(<$testcounter_file) + if [ $ok -eq 1 ]; then + echo -e "${green}ok $count $testname${reset}" >&3 + return + fi + + # Failed + local expect=$3 + local actual=$4 + echo -e "${red}not ok $count $testname${reset}" >&3 + echo -e "${red}# expected: $expect${reset}" >&3 + echo -e "${red}# actual: ${bold}$actual${reset}" >&3 + + _bump $failures_file +} + +########### +# _bump # Increment a counter in a file +########### +function _bump() { + local file=$1 + + count=$(<$file) + echo $(( $count + 1 )) >| $file +} + +############# +# jsonify # convert 'foo=bar,x=y' to json {"foo":"bar","x":"y"} +############# +function jsonify() { + # split by comma + local -a settings_in + read -ra settings_in <<<"$1" + + # convert each to double-quoted form + local -a settings_out + for i in ${settings_in[*]}; do + settings_out+=$(sed -e 's/\(.*\)=\(.*\)/"\1":"\2"/' <<<$i) + done + + # ...and wrap inside braces. + # FIXME: handle commas + echo "{${settings_out[*]}}" +} + +####### +# t # Main test helper +####### +function t() { + local method=$1; shift + local path=$1; shift + local curl_args + + local testname="$method $path" + # POST requests require an extra params arg + if [[ $method = "POST" ]]; then + curl_args="-d $(jsonify $1)" + testname="$testname [$1]" + shift + fi + # curl -X HEAD but without --head seems to wait for output anyway + if [[ $method == "HEAD" ]]; then + curl_args="--head" + fi + local expected_code=$1; shift + + # If given path begins with /, use it as-is; otherwise prepend /version/ + local url=http://$HOST:$PORT + if expr "$path" : "/" >/dev/null; then + url="$url$path" + else + url="$url/v1.40/$path" + fi + + # Log every action we do + echo "-------------------------------------------------------------" >>$LOG + echo "\$ $testname" >>$LOG + rm -f $WORKDIR/curl.* + curl -s -X $method ${curl_args} \ + -H 'Content-type: application/json' \ + --dump-header $WORKDIR/curl.headers.out \ + -o $WORKDIR/curl.result.out "$url" + + if [[ $? -eq 7 ]]; then + echo "FATAL: curl failure on $url - cannot continue" >&2 + exit 1 + fi + + cat $WORKDIR/curl.headers.out $WORKDIR/curl.result.out >>$LOG 2>/dev/null || true + + # Test return code + actual_code=$(head -n1 $WORKDIR/curl.headers.out | awk '/^HTTP/ { print $2}') + is "$actual_code" "$expected_code" "$testname : status" + + output=$(< $WORKDIR/curl.result.out) + + for i; do + case "$i" in + # Exact match on json field + .*=*) + json_field=$(expr "$i" : "\([^=]*\)=") + expect=$(expr "$i" : '[^=]*=\(.*\)') + actual=$(jq -r "$json_field" <<<"$output") + is "$actual" "$expect" "$testname : $json_field" + ;; + # regex match on json field + .*~*) + json_field=$(expr "$i" : "\([^~]*\)~") + expect=$(expr "$i" : '[^~]*~\(.*\)') + actual=$(jq -r "$json_field" <<<"$output") + like "$actual" "$expect" "$testname : $json_field" + ;; + # Direct string comparison + *) + is "$output" "$i" "$testname : output" + ;; + esac + done +} + +################### +# start_service # Run the socket listener +################### +service_pid= +function start_service() { + # If there's a listener on the port, nothing for us to do + echo -n >/dev/tcp/$HOST/$PORT &>/dev/null && return + + if [ "$HOST" != "localhost" ]; then + die "Cannot start service on non-localhost ($HOST)" + fi + + if [ $(id -u) -ne 0 ]; then + echo "$ME: WARNING: running service rootless is unlikely to work!" >&2 + fi + + # Find the binary + SERVICE_BIN=${SERVICE_BIN:-${TESTS_DIR}/../../bin/service} + test -x $SERVICE_BIN || die "Not found: $SERVICE_BIN" + + systemd-socket-activate -l 127.0.0.1:$PORT \ + $SERVICE_BIN --root $WORKDIR/root \ + &> $WORKDIR/server.log & + service_pid=$! + + # Wait + local _timeout=5 + while [ $_timeout -gt 0 ]; do + echo -n >/dev/tcp/$HOST/$PORT &>/dev/null && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + die "Timed out waiting for service" +} + +# END infrastructure code +############################################################################### +# BEGIN sanity checks + +for tool in curl jq podman; do + type $tool &>/dev/null || die "$ME: Required tool '$tool' not found" +done + +# END sanity checks +############################################################################### +# BEGIN entry handler (subtest invoker) + +# Identify the tests to run. If called with args, use those as globs. +tests_to_run=() +if [ -n "$*" ]; then + shopt -s nullglob + for i; do + match=(${TESTS_DIR}/*${i}*.at) + if [ ${#match} -eq 0 ]; then + die "No match for $TESTS_DIR/*$i*.at" + fi + tests_to_run+=("${match[@]}") + done + shopt -u nullglob +else + tests_to_run=($TESTS_DIR/*.at) +fi + +# Because subtests may run podman or other commands that emit stderr; +# redirect all those and use fd 3 for all output +exec 3>&1 &>$WORKDIR/output.log + +start_service + +for i in ${tests_to_run[@]}; do + source $i +done + +# END entry handler +############################################################################### + +# Clean up + +if [ -n "$service_pid" ]; then + # Yep, has to be -9. It ignores everything else. + kill -9 $service_pid +fi + +test_count=$(<$testcounter_file) +failure_count=$(<$failures_file) + +if [ $failure_count -gt 0 -a -s "$WORKDIR/output.log" ]; then + echo "# Collected stdout/stderr:" >&3 + sed -e 's/^/# /' < $WORKDIR/output.log >&3 +fi + +if [ -z "$PODMAN_TESTS_KEEP_WORKDIR" ]; then + rm -rf $WORKDIR +fi + +echo "1..${test_count}" >&3 + +exit $failure_count diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 9d23384ea..ebac087ac 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -164,4 +164,17 @@ var _ = Describe("Podman inspect", func() { Expect(inspectDst.ExitCode()).To(Equal(0)) Expect(inspectDst.OutputToString()).To(Equal("/test1")) }) + + It("podman inspect shows healthcheck on docker image", func() { + pull := podmanTest.Podman([]string{"pull", healthcheck}) + pull.WaitWithDefaultTimeout() + Expect(pull.ExitCode()).To(BeZero()) + + session := podmanTest.Podman([]string{"inspect", "--format=json", healthcheck}) + session.WaitWithDefaultTimeout() + imageData := session.InspectImageJSON() + Expect(imageData[0].HealthCheck.Timeout).To(BeNumerically("==", 3000000000)) + Expect(imageData[0].HealthCheck.Interval).To(BeNumerically("==", 60000000000)) + Expect(imageData[0].HealthCheck.Test).To(Equal([]string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"})) + }) }) |