summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--cmd/podman/common.go2
-rw-r--r--cmd/podman/push.go5
-rw-r--r--cmd/podman/shared/pod.go56
-rw-r--r--contrib/systemd/system/podman-docker.conf1
-rw-r--r--docs/source/markdown/podman-container-exists.1.md4
-rw-r--r--docs/source/markdown/podman-exec.1.md12
-rw-r--r--docs/source/markdown/podman-history.1.md12
-rw-r--r--docs/source/markdown/podman-image-exists.1.md4
-rw-r--r--docs/source/markdown/podman-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-remote.1.md18
-rw-r--r--docs/source/markdown/podman-rm.1.md33
-rw-r--r--docs/source/markdown/podman-rmi.1.md19
-rw-r--r--docs/source/markdown/podman-run.1.md8
-rw-r--r--docs/source/markdown/podman-stop.1.md20
-rw-r--r--docs/source/markdown/podman-system-df.1.md4
-rw-r--r--docs/source/markdown/podman-system-prune.1.md6
-rw-r--r--docs/source/markdown/podman.1.md18
-rw-r--r--docs/source/network.rst2
-rw-r--r--go.mod6
-rw-r--r--go.sum4
-rw-r--r--install.md22
-rw-r--r--libpod/container_inspect.go3
-rw-r--r--libpod/define/podstate.go19
-rw-r--r--libpod/image/image.go9
-rw-r--r--libpod/pod_status.go59
-rw-r--r--libpod/runtime_pod.go28
-rw-r--r--pkg/adapter/pods.go4
-rw-r--r--pkg/adapter/pods_remote.go4
-rw-r--r--pkg/api/handlers/containers_create.go (renamed from pkg/api/handlers/generic/containers_create.go)9
-rw-r--r--pkg/api/handlers/decoder.go91
-rw-r--r--pkg/api/handlers/events.go25
-rw-r--r--pkg/api/handlers/generic/config.go9
-rw-r--r--pkg/api/handlers/generic/images.go73
-rw-r--r--pkg/api/handlers/generic/ping.go4
-rw-r--r--pkg/api/handlers/generic/swagger.go4
-rw-r--r--pkg/api/handlers/handler.go9
-rw-r--r--pkg/api/handlers/images.go41
-rw-r--r--pkg/api/handlers/libpod/containers.go6
-rw-r--r--pkg/api/handlers/libpod/images.go78
-rw-r--r--pkg/api/handlers/libpod/pods.go53
-rw-r--r--pkg/api/handlers/types.go8
-rw-r--r--pkg/api/handlers/utils/errors.go11
-rw-r--r--pkg/api/handlers/utils/images.go17
-rw-r--r--pkg/api/handlers/version.go (renamed from pkg/api/handlers/generic/version.go)5
-rw-r--r--pkg/api/server/docs.go3
-rw-r--r--pkg/api/server/register_containers.go4
-rw-r--r--pkg/api/server/register_images.go61
-rw-r--r--pkg/api/server/register_pods.go16
-rw-r--r--pkg/api/server/register_version.go6
-rw-r--r--pkg/api/server/server.go7
-rw-r--r--pkg/bindings/connection.go180
-rw-r--r--pkg/bindings/containers.go139
-rw-r--r--pkg/bindings/containers/containers.go255
-rw-r--r--pkg/bindings/containers/healthcheck.go26
-rw-r--r--pkg/bindings/containers/mount.go53
-rw-r--r--pkg/bindings/errors.go11
-rw-r--r--pkg/bindings/generate.go4
-rw-r--r--pkg/bindings/generate/generate.go4
-rw-r--r--pkg/bindings/healthcheck.go19
-rw-r--r--pkg/bindings/images.go111
-rw-r--r--pkg/bindings/images/images.go187
-rw-r--r--pkg/bindings/images/search.go40
-rw-r--r--pkg/bindings/mount.go26
-rw-r--r--pkg/bindings/network.go37
-rw-r--r--pkg/bindings/network/network.go50
-rw-r--r--pkg/bindings/play.go3
-rw-r--r--pkg/bindings/play/play.go7
-rw-r--r--pkg/bindings/pods.go129
-rw-r--r--pkg/bindings/pods/pods.go196
-rw-r--r--pkg/bindings/search.go39
-rw-r--r--pkg/bindings/test/common_test.go112
-rw-r--r--pkg/bindings/test/images_test.go92
-rw-r--r--pkg/bindings/volumes.go60
-rw-r--r--pkg/bindings/volumes/volumes.go85
-rw-r--r--pkg/inspect/inspect.go46
-rw-r--r--pkg/util/camelcase/LICENSE.md (renamed from vendor/github.com/fatih/camelcase/LICENSE.md)0
-rw-r--r--pkg/util/camelcase/README.md (renamed from vendor/github.com/fatih/camelcase/README.md)0
-rw-r--r--pkg/util/camelcase/camelcase.go (renamed from vendor/github.com/fatih/camelcase/camelcase.go)5
-rw-r--r--rootless.md1
-rw-r--r--test/apiv2/00-TEMPLATE6
-rw-r--r--test/apiv2/01-basic.at49
-rw-r--r--test/apiv2/10-images.at36
-rw-r--r--test/apiv2/20-containers.at29
-rw-r--r--test/apiv2/30-volumes.at14
-rw-r--r--test/apiv2/40-pods.at33
-rw-r--r--test/apiv2/README.md63
-rwxr-xr-xtest/apiv2/test-apiv2325
-rw-r--r--test/e2e/inspect_test.go13
-rw-r--r--vendor/github.com/fatih/camelcase/.travis.yml3
-rw-r--r--vendor/gopkg.in/yaml.v2/scannerc.go47
-rw-r--r--vendor/gopkg.in/yaml.v2/yamlh.go1
-rw-r--r--vendor/modules.txt6
93 files changed, 2456 insertions, 1014 deletions
diff --git a/Makefile b/Makefile
index a7e779dd2..85f1036e0 100644
--- a/Makefile
+++ b/Makefile
@@ -263,7 +263,7 @@ localunit: test/goecho/goecho varlink_generate
ginkgo \
-r \
$(TESTFLAGS) \
- --skipPackage test/e2e,pkg/apparmor,test/endpoint \
+ --skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings \
--cover \
--covermode atomic \
--tags "$(BUILDTAGS)" \
@@ -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/common.go b/cmd/podman/common.go
index f3aff1d49..46feae90d 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -12,7 +12,7 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/sysinfo"
- "github.com/fatih/camelcase"
+ "github.com/containers/libpod/pkg/util/camelcase"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/spf13/cobra"
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/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index d8d69c8fc..7b0b497fc 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -10,14 +10,8 @@ import (
"github.com/pkg/errors"
)
-const (
- PodStateStopped = "Stopped"
- PodStateRunning = "Running"
- PodStatePaused = "Paused"
- PodStateExited = "Exited"
- PodStateErrored = "Error"
- PodStateCreated = "Created"
-)
+// TODO GetPodStatus and CreatePodStatusResults should removed once the adapter
+// and shared packages are reworked. It has now been duplicated in libpod proper.
// GetPodStatus determines the status of the pod based on the
// statuses of the containers in the pod.
@@ -25,7 +19,7 @@ const (
func GetPodStatus(pod *libpod.Pod) (string, error) {
ctrStatuses, err := pod.Status()
if err != nil {
- return PodStateErrored, err
+ return define.PodStateErrored, err
}
return CreatePodStatusResults(ctrStatuses)
}
@@ -33,45 +27,45 @@ func GetPodStatus(pod *libpod.Pod) (string, error) {
func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) {
ctrNum := len(ctrStatuses)
if ctrNum == 0 {
- return PodStateCreated, nil
+ return define.PodStateCreated, nil
}
statuses := map[string]int{
- PodStateStopped: 0,
- PodStateRunning: 0,
- PodStatePaused: 0,
- PodStateCreated: 0,
- PodStateErrored: 0,
+ define.PodStateStopped: 0,
+ define.PodStateRunning: 0,
+ define.PodStatePaused: 0,
+ define.PodStateCreated: 0,
+ define.PodStateErrored: 0,
}
for _, ctrStatus := range ctrStatuses {
switch ctrStatus {
case define.ContainerStateExited:
fallthrough
case define.ContainerStateStopped:
- statuses[PodStateStopped]++
+ statuses[define.PodStateStopped]++
case define.ContainerStateRunning:
- statuses[PodStateRunning]++
+ statuses[define.PodStateRunning]++
case define.ContainerStatePaused:
- statuses[PodStatePaused]++
+ statuses[define.PodStatePaused]++
case define.ContainerStateCreated, define.ContainerStateConfigured:
- statuses[PodStateCreated]++
+ statuses[define.PodStateCreated]++
default:
- statuses[PodStateErrored]++
+ statuses[define.PodStateErrored]++
}
}
switch {
- case statuses[PodStateRunning] > 0:
- return PodStateRunning, nil
- case statuses[PodStatePaused] == ctrNum:
- return PodStatePaused, nil
- case statuses[PodStateStopped] == ctrNum:
- return PodStateExited, nil
- case statuses[PodStateStopped] > 0:
- return PodStateStopped, nil
- case statuses[PodStateErrored] > 0:
- return PodStateErrored, nil
+ case statuses[define.PodStateRunning] > 0:
+ return define.PodStateRunning, nil
+ case statuses[define.PodStatePaused] == ctrNum:
+ return define.PodStatePaused, nil
+ case statuses[define.PodStateStopped] == ctrNum:
+ return define.PodStateExited, nil
+ case statuses[define.PodStateStopped] > 0:
+ return define.PodStateStopped, nil
+ case statuses[define.PodStateErrored] > 0:
+ return define.PodStateErrored, nil
default:
- return PodStateCreated, nil
+ return define.PodStateCreated, nil
}
}
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-exec.1.md b/docs/source/markdown/podman-exec.1.md
index 8c0106d70..1bd10f9ba 100644
--- a/docs/source/markdown/podman-exec.1.md
+++ b/docs/source/markdown/podman-exec.1.md
@@ -80,28 +80,28 @@ when creating the container.
The exit code from `podman exec` gives information about why the command within the container failed to run or why it exited. When `podman exec` exits with a
non-zero code, the exit codes follow the `chroot` standard, see below:
-**_125_** if the error is with Podman **_itself_**
+ **125** The error is with Podman itself
$ podman exec --foo ctrID /bin/sh; echo $?
Error: unknown flag: --foo
125
-**_126_** if the **_contained command_** cannot be invoked
+ **126** The _contained command_ cannot be invoked
$ podman exec ctrID /etc; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error
126
-**_127_** if the **_contained command_** cannot be found
+ **127** The _contained command_ cannot be found
$ podman exec ctrID foo; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error
127
-**_Exit code_** of **_contained command_** otherwise
+ **Exit code** The _contained command_ exit code
- $ podman exec ctrID /bin/sh -c 'exit 3'
- # 3
+ $ podman exec ctrID /bin/sh -c 'exit 3'; echo $?
+ 3
## EXAMPLES
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-remote.1.md b/docs/source/markdown/podman-remote.1.md
index bbc54a2a6..eab2405b2 100644
--- a/docs/source/markdown/podman-remote.1.md
+++ b/docs/source/markdown/podman-remote.1.md
@@ -65,27 +65,27 @@ The exit code from `podman` gives information about why the container
failed to run or why it exited. When `podman` commands exit with a non-zero code,
the exit codes follow the `chroot` standard, see below:
-**_125_** if the error is with podman **_itself_**
+ **125** The error is with podman itself
$ podman run --foo busybox; echo $?
Error: unknown flag: --foo
- 125
+ 125
-**_126_** if executing a **_contained command_** and the **_command_** cannot be invoked
+ **126** Executing a _contained command_ and the _command_ cannot be invoked
$ podman run busybox /etc; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error
- 126
+ 126
-**_127_** if executing a **_contained command_** and the **_command_** cannot be found
+ **127** Executing a _contained command_ and the _command_ cannot be found
$ podman run busybox foo; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error
- 127
+ 127
-**_Exit code_** of **_contained command_** otherwise
+ **Exit code** _contained command_ exit code
- $ podman run busybox /bin/sh -c 'exit 3'
- # 3
+ $ podman run busybox /bin/sh -c 'exit 3'; echo $?
+ 3
## COMMANDS
diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md
index 782feac6f..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,51 +46,54 @@ 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
-**_0_** if all specified containers removed
-**_1_** if one of the specified containers did not exist, and no other failures
-**_2_** if one of the specified containers is paused or running
-**_125_** if the command fails for a reason other than container did not exist or is paused/running
+ **0** All specified containers removed
+
+ **1** One of the specified containers did not exist, and no other failures
+
+ **2** One of the specified containers is paused or running
+
+ **125** The command fails for a reason other than container did not exist or is paused/running
## SEE ALSO
podman(1), podman-image-rm(1)
diff --git a/docs/source/markdown/podman-rmi.1.md b/docs/source/markdown/podman-rmi.1.md
index 3c46bc32c..78ef2b157 100644
--- a/docs/source/markdown/podman-rmi.1.md
+++ b/docs/source/markdown/podman-rmi.1.md
@@ -24,27 +24,30 @@ 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_** if all specified images removed
-**_1_** if one of the specified images did not exist, and no other failures
-**_2_** if one of the specified images has child images or is being used by a container
-**_125_** if the command fails for a reason other than an image did not exist or is in use
+ **0** All specified images removed
+
+ **1** One of the specified images did not exist, and no other failures
+
+ **2** One of the specified images has child images or is being used by a container
+
+ **125** The command fails for a reason other than an image did not exist or is in use
## SEE ALSO
podman(1)
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 512a382a6..bf79ea031 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -1022,25 +1022,25 @@ The exit code from `podman run` gives information about why the container
failed to run or why it exited. When `podman run` exits with a non-zero code,
the exit codes follow the `chroot` standard, see below:
-**_125_** if the error is with Podman **_itself_**
+ **125** The error is with Podman itself
$ podman run --foo busybox; echo $?
Error: unknown flag: --foo
125
-**_126_** if the **_contained command_** cannot be invoked
+ **126** The _contained command_ cannot be invoked
$ podman run busybox /etc; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error
126
-**_127_** if the **_contained command_** cannot be found
+ **127** The _contained command_ cannot be found
$ podman run busybox foo; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error
127
-**_Exit code_** of **_contained command_** otherwise
+ **Exit code** _contained command_ exit code
$ podman run busybox /bin/sh -c 'exit 3'
3
diff --git a/docs/source/markdown/podman-stop.1.md b/docs/source/markdown/podman-stop.1.md
index 3b5f17057..7dbf18887 100644
--- a/docs/source/markdown/podman-stop.1.md
+++ b/docs/source/markdown/podman-stop.1.md
@@ -38,27 +38,27 @@ to run containers such as CRI-O, the last started container could be from either
The latest option is not supported on the remote client.
-**--timeout**, **--time**, **t**=*time*
+**--timeout**, **--time**, **-t**=*time*
Timeout to wait before forcibly stopping the container
-## EXAMPLE
+## EXAMPLES
-podman stop mywebserver
+$ podman stop mywebserver
-podman stop 860a4b235279
+$ podman stop 860a4b235279
-podman stop mywebserver 860a4b235279
+$ podman stop mywebserver 860a4b235279
-podman stop --cidfile /home/user/cidfile-1
+$ podman stop --cidfile /home/user/cidfile-1
-podman stop --cidfile /home/user/cidfile-1 --cidfile ./cidfile-2
+$ podman stop --cidfile /home/user/cidfile-1 --cidfile ./cidfile-2
-podman stop --timeout 2 860a4b235279
+$ podman stop --timeout 2 860a4b235279
-podman stop -a
+$ podman stop -a
-podman stop --latest
+$ podman stop --latest
## SEE ALSO
podman(1), podman-rm(1)
diff --git a/docs/source/markdown/podman-system-df.1.md b/docs/source/markdown/podman-system-df.1.md
index d0b1755ee..3ddb685fc 100644
--- a/docs/source/markdown/podman-system-df.1.md
+++ b/docs/source/markdown/podman-system-df.1.md
@@ -18,7 +18,7 @@ Pretty-print images using a Go template
Show detailed information on space usage
## EXAMPLE
-
+```
$ podman system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 6 2 281MB 168MB (59%)
@@ -49,7 +49,7 @@ $ podman system df --format "{{.Type}}\t{{.Total}}"
Images 1
Containers 5
Local Volumes 1
-
+```
## SEE ALSO
podman-system(1)
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/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 6e0eff045..acb546ddd 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -126,27 +126,27 @@ The exit code from `podman` gives information about why the container
failed to run or why it exited. When `podman` commands exit with a non-zero code,
the exit codes follow the `chroot` standard, see below:
-**_125_** if the error is with podman **_itself_**
+ **125** The error is with podman **_itself_**
$ podman run --foo busybox; echo $?
Error: unknown flag: --foo
- 125
+ 125
-**_126_** if executing a **_contained command_** and the **_command_** cannot be invoked
+ **126** Executing a _contained command_ and the _command_ cannot be invoked
$ podman run busybox /etc; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error
- 126
+ 126
-**_127_** if executing a **_contained command_** and the **_command_** cannot be found
+ **127** Executing a _contained command_ and the _command_ cannot be found
$ podman run busybox foo; echo $?
Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error
- 127
+ 127
-**_Exit code_** of **_contained command_** otherwise
+ **Exit code** _contained command_ exit code
- $ podman run busybox /bin/sh -c 'exit 3'
- # 3
+ $ podman run busybox /bin/sh -c 'exit 3'; echo $?
+ 3
## COMMANDS
diff --git a/docs/source/network.rst b/docs/source/network.rst
index d96e00a7d..e7848c90e 100644
--- a/docs/source/network.rst
+++ b/docs/source/network.rst
@@ -1,5 +1,5 @@
Network
-=====
+=======
:doc:`create <markdown/podman-network-create.1>` network create
diff --git a/go.mod b/go.mod
index 58aa30b5a..c91d8b4c9 100644
--- a/go.mod
+++ b/go.mod
@@ -29,7 +29,7 @@ require (
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1 // indirect
github.com/etcd-io/bbolt v1.3.3
- github.com/fatih/camelcase v1.0.0
+ github.com/fatih/camelcase v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/ghodss/yaml v1.0.0
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e
@@ -54,7 +54,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0
- github.com/rootless-containers/rootlesskit v0.7.1
+ github.com/rootless-containers/rootlesskit v0.7.2
github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
@@ -72,7 +72,7 @@ require (
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
google.golang.org/appengine v1.6.1 // indirect
google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 // indirect
- gopkg.in/yaml.v2 v2.2.7
+ gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab
diff --git a/go.sum b/go.sum
index ff350fa1f..3a823b21b 100644
--- a/go.sum
+++ b/go.sum
@@ -456,6 +456,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rootless-containers/rootlesskit v0.7.1 h1:enhwHIAXDjfpV83bL4xF60WZ+1ATjMB7spDDvpWAfPk=
github.com/rootless-containers/rootlesskit v0.7.1/go.mod h1:r9YL5mKRIdnwcYk4G8E5CSc9MDeFtgYmhfE4CSvDGYA=
+github.com/rootless-containers/rootlesskit v0.7.2 h1:gcWQ9/GN98ne1AqnoeOgQ8e6qpKd3BuB4ug+2h95Fr0=
+github.com/rootless-containers/rootlesskit v0.7.2/go.mod h1:r9YL5mKRIdnwcYk4G8E5CSc9MDeFtgYmhfE4CSvDGYA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
@@ -698,6 +700,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v0.0.0-20190624233834-05ebafbffc79/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/install.md b/install.md
index 90ad4f233..561c4afe9 100644
--- a/install.md
+++ b/install.md
@@ -35,8 +35,6 @@ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:st
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
-sudo mkdir -p /etc/containers
-echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```
There are many [packages](https://packages.debian.org/search?keywords=libpod&searchon=names&suite=stable&section=all)
@@ -68,6 +66,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
@@ -90,8 +98,6 @@ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:st
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
-sudo mkdir -p /etc/containers
-echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```
@@ -159,8 +165,6 @@ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:te
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
-sudo mkdir -p /etc/containers
-echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```
@@ -177,6 +181,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)
@@ -189,8 +195,6 @@ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:te
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
-sudo mkdir -p /etc/containers
-echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```
@@ -205,8 +209,6 @@ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:te
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
-sudo mkdir -p /etc/containers
-echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 01f2d93bd..641bc8a91 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -1014,6 +1014,9 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.ShmSize = c.config.ShmSize
hostConfig.Runtime = "oci"
+ // Default CPUShares is 1024, but we may overwrite below.
+ hostConfig.CpuShares = 1024
+
// This is very expensive to initialize.
// So we don't want to initialize it unless we absolutely have to - IE,
// there are things that require a major:minor to path translation.
diff --git a/libpod/define/podstate.go b/libpod/define/podstate.go
new file mode 100644
index 000000000..2b59aabfb
--- /dev/null
+++ b/libpod/define/podstate.go
@@ -0,0 +1,19 @@
+package define
+
+const (
+ // PodStateCreated indicates the pod is created but has not been started
+ PodStateCreated = "Created"
+ // PodStateErrored indicates the pod is in an errored state where
+ // information about it can no longer be retrieved
+ PodStateErrored = "Error"
+ // PodStateExited indicates the pod ran but has been stopped
+ PodStateExited = "Exited"
+ // PodStatePaused indicates the pod has been paused
+ PodStatePaused = "Paused"
+ // PodStateRunning indicates that one or more of the containers in
+ // the pod is running
+ PodStateRunning = "Running"
+ // PodStateStopped indicates all of the containers belonging to the pod
+ // are stopped.
+ PodStateStopped = "Stopped"
+)
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/libpod/pod_status.go b/libpod/pod_status.go
new file mode 100644
index 000000000..3a44c4457
--- /dev/null
+++ b/libpod/pod_status.go
@@ -0,0 +1,59 @@
+package libpod
+
+import "github.com/containers/libpod/libpod/define"
+
+// GetPodStatus determines the status of the pod based on the
+// statuses of the containers in the pod.
+// Returns a string representation of the pod status
+func (p *Pod) GetPodStatus() (string, error) {
+ ctrStatuses, err := p.Status()
+ if err != nil {
+ return define.PodStateErrored, err
+ }
+ return CreatePodStatusResults(ctrStatuses)
+}
+
+func CreatePodStatusResults(ctrStatuses map[string]define.ContainerStatus) (string, error) {
+ ctrNum := len(ctrStatuses)
+ if ctrNum == 0 {
+ return define.PodStateCreated, nil
+ }
+ statuses := map[string]int{
+ define.PodStateStopped: 0,
+ define.PodStateRunning: 0,
+ define.PodStatePaused: 0,
+ define.PodStateCreated: 0,
+ define.PodStateErrored: 0,
+ }
+ for _, ctrStatus := range ctrStatuses {
+ switch ctrStatus {
+ case define.ContainerStateExited:
+ fallthrough
+ case define.ContainerStateStopped:
+ statuses[define.PodStateStopped]++
+ case define.ContainerStateRunning:
+ statuses[define.PodStateRunning]++
+ case define.ContainerStatePaused:
+ statuses[define.PodStatePaused]++
+ case define.ContainerStateCreated, define.ContainerStateConfigured:
+ statuses[define.PodStateCreated]++
+ default:
+ statuses[define.PodStateErrored]++
+ }
+ }
+
+ switch {
+ case statuses[define.PodStateRunning] > 0:
+ return define.PodStateRunning, nil
+ case statuses[define.PodStatePaused] == ctrNum:
+ return define.PodStatePaused, nil
+ case statuses[define.PodStateStopped] == ctrNum:
+ return define.PodStateExited, nil
+ case statuses[define.PodStateStopped] > 0:
+ return define.PodStateStopped, nil
+ case statuses[define.PodStateErrored] > 0:
+ return define.PodStateErrored, nil
+ default:
+ return define.PodStateCreated, nil
+ }
+}
diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go
index 66f9b10c9..e1dc31391 100644
--- a/libpod/runtime_pod.go
+++ b/libpod/runtime_pod.go
@@ -182,3 +182,31 @@ func (r *Runtime) GetRunningPods() ([]*Pod, error) {
}
return runningPods, nil
}
+
+// PrunePods removes unused pods and their containers from local storage.
+// If force is given, then running pods are also included in the pruning.
+func (r *Runtime) PrunePods() (map[string]error, error) {
+ response := make(map[string]error)
+ states := []string{define.PodStateStopped, define.PodStateExited}
+ filterFunc := func(p *Pod) bool {
+ state, _ := p.GetPodStatus()
+ for _, status := range states {
+ if state == status {
+ return true
+ }
+ }
+ return false
+ }
+ pods, err := r.Pods(filterFunc)
+ if err != nil {
+ return nil, err
+ }
+ if len(pods) < 1 {
+ return response, nil
+ }
+ for _, pod := range pods {
+ err := r.removePod(context.TODO(), pod, true, false)
+ response[pod.ID()] = err
+ }
+ return response, nil
+}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 5891c361f..f89fc7011 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -58,9 +58,9 @@ func (r *LocalRuntime) PrunePods(ctx context.Context, cli *cliconfig.PodPruneVal
}
logrus.Debugf("Setting maximum rm workers to %d", maxWorkers)
- states := []string{shared.PodStateStopped, shared.PodStateExited}
+ states := []string{define.PodStateStopped, define.PodStateExited}
if cli.Force {
- states = append(states, shared.PodStateRunning)
+ states = append(states, define.PodStateRunning)
}
pods, err := r.GetPodsByStatus(states)
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 16d34769e..5ef1a9216 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -540,9 +540,9 @@ func (r *LocalRuntime) PrunePods(ctx context.Context, cli *cliconfig.PodPruneVal
ok = []string{}
failures = map[string]error{}
)
- states := []string{shared.PodStateStopped, shared.PodStateExited}
+ states := []string{define.PodStateStopped, define.PodStateExited}
if cli.Force {
- states = append(states, shared.PodStateRunning)
+ states = append(states, define.PodStateRunning)
}
ids, err := iopodman.GetPodsByStatus().Call(r.Conn, states)
diff --git a/pkg/api/handlers/generic/containers_create.go b/pkg/api/handlers/containers_create.go
index edefd5757..4781b23bc 100644
--- a/pkg/api/handlers/generic/containers_create.go
+++ b/pkg/api/handlers/containers_create.go
@@ -1,4 +1,4 @@
-package generic
+package handlers
import (
"encoding/json"
@@ -10,7 +10,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
image2 "github.com/containers/libpod/libpod/image"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/namespaces"
createconfig "github.com/containers/libpod/pkg/spec"
@@ -25,7 +24,7 @@ import (
func CreateContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
- input := handlers.CreateContainerConfig{}
+ input := CreateContainerConfig{}
query := struct {
Name string `schema:"name"`
}{
@@ -74,13 +73,13 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
}
response := ContainerCreateResponse{
- Id: ctr.ID(),
+ ID: ctr.ID(),
Warnings: []string{}}
utils.WriteResponse(w, http.StatusCreated, response)
}
-func makeCreateConfig(input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
+func makeCreateConfig(input CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
var (
err error
init bool
diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go
new file mode 100644
index 000000000..890d77ecc
--- /dev/null
+++ b/pkg/api/handlers/decoder.go
@@ -0,0 +1,91 @@
+package handlers
+
+import (
+ "encoding/json"
+ "reflect"
+ "time"
+
+ "github.com/gorilla/schema"
+ "github.com/sirupsen/logrus"
+)
+
+// NewAPIDecoder returns a configured schema.Decoder
+func NewAPIDecoder() *schema.Decoder {
+ _ = ParseDateTime
+
+ d := schema.NewDecoder()
+ d.IgnoreUnknownKeys(true)
+ d.RegisterConverter(map[string][]string{}, convertUrlValuesString)
+ d.RegisterConverter(time.Time{}, convertTimeString)
+ return d
+}
+
+// On client:
+// v := map[string][]string{
+// "dangling": {"true"},
+// }
+//
+// payload, err := jsoniter.MarshalToString(v)
+// if err != nil {
+// panic(err)
+// }
+// payload = url.QueryEscape(payload)
+func convertUrlValuesString(query string) reflect.Value {
+ f := map[string][]string{}
+
+ err := json.Unmarshal([]byte(query), &f)
+ if err != nil {
+ logrus.Infof("convertUrlValuesString: Failed to Unmarshal %s: %s", query, err.Error())
+ }
+
+ return reflect.ValueOf(f)
+}
+
+// isZero() can be used to determine if parsing failed.
+func convertTimeString(query string) reflect.Value {
+ var (
+ err error
+ t time.Time
+ )
+
+ for _, f := range []string{
+ time.UnixDate,
+ time.ANSIC,
+ time.RFC1123,
+ time.RFC1123Z,
+ time.RFC3339,
+ time.RFC3339Nano,
+ time.RFC822,
+ time.RFC822Z,
+ time.RFC850,
+ time.RubyDate,
+ time.Stamp,
+ time.StampMicro,
+ time.StampMilli,
+ time.StampNano,
+ } {
+ t, err = time.Parse(f, query)
+ if err == nil {
+ return reflect.ValueOf(t)
+ }
+
+ if _, isParseError := err.(*time.ParseError); isParseError {
+ // Try next format
+ continue
+ } else {
+ break
+ }
+ }
+
+ // We've exhausted all formats, or something bad happened
+ if err != nil {
+ logrus.Infof("convertTimeString: Failed to parse %s: %s", query, err.Error())
+ }
+ return reflect.ValueOf(time.Time{})
+}
+
+// ParseDateTime is a helper function to aid in parsing different Time/Date formats
+// isZero() can be used to determine if parsing failed.
+func ParseDateTime(query string) time.Time {
+ return convertTimeString(query).Interface().(time.Time)
+}
diff --git a/pkg/api/handlers/events.go b/pkg/api/handlers/events.go
index 900efa3da..44bf35254 100644
--- a/pkg/api/handlers/events.go
+++ b/pkg/api/handlers/events.go
@@ -1,9 +1,10 @@
package handlers
import (
- "encoding/json"
"fmt"
"net/http"
+ "strings"
+ "time"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/pkg/errors"
@@ -11,30 +12,24 @@ import (
func GetEvents(w http.ResponseWriter, r *http.Request) {
query := struct {
- Since string `json:"since"`
- Until string `json:"until"`
- Filters string `json:"filters"`
+ Since time.Time `schema:"since"`
+ Until time.Time `schema:"until"`
+ Filters map[string][]string `schema:"filters"`
}{}
if err := decodeQuery(r, &query); err != nil {
utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
}
- var filters = map[string][]string{}
- if found := hasVar(r, "filters"); found {
- if err := json.Unmarshal([]byte(query.Filters), &filters); err != nil {
- utils.BadRequest(w, "filters", query.Filters, err)
- return
+ var libpodFilters = []string{}
+ if _, found := r.URL.Query()["filters"]; found {
+ for k, v := range query.Filters {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
}
- var libpodFilters = make([]string, len(filters))
- for k, v := range filters {
- libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
- }
-
libpodEvents, err := getRuntime(r).GetEvents(libpodFilters)
if err != nil {
- utils.BadRequest(w, "filters", query.Filters, err)
+ utils.BadRequest(w, "filters", strings.Join(r.URL.Query()["filters"], ", "), err)
return
}
diff --git a/pkg/api/handlers/generic/config.go b/pkg/api/handlers/generic/config.go
deleted file mode 100644
index f715d25eb..000000000
--- a/pkg/api/handlers/generic/config.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package generic
-
-// ContainerCreateResponse is the response struct for creating a container
-type ContainerCreateResponse struct {
- // ID of the container created
- Id string `json:"Id"`
- // Warnings during container creation
- Warnings []string `json:"Warnings"`
-}
diff --git a/pkg/api/handlers/generic/images.go b/pkg/api/handlers/generic/images.go
index 395f64064..ba1cf47b4 100644
--- a/pkg/api/handlers/generic/images.go
+++ b/pkg/api/handlers/generic/images.go
@@ -6,7 +6,6 @@ import (
"io/ioutil"
"net/http"
"os"
- "strconv"
"strings"
"github.com/containers/buildah"
@@ -16,12 +15,10 @@ import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/util"
- "github.com/containers/storage"
"github.com/docker/docker/api/types"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
func ExportImage(w http.ResponseWriter, r *http.Request) {
@@ -59,17 +56,14 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
}
func PruneImages(w http.ResponseWriter, r *http.Request) {
- // 200 no error
- // 500 internal
var (
- dangling bool = true
- err error
+ filters []string
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
- filters map[string]string
+ Filters map[string][]string `schema:"filters"`
}{
// This is where you can override the golang default value for one of fields
}
@@ -79,61 +73,24 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
return
}
- // FIXME This is likely wrong due to it not being a map[string][]string
-
- // until ts is not supported on podman prune
- if len(query.filters["until"]) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "until is not supported yet"))
- return
- }
- // labels are not supported on podman prune
- if len(query.filters["label"]) > 0 {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "labelis not supported yet"))
- return
- }
-
- if len(query.filters["dangling"]) > 0 {
- dangling, err = strconv.ParseBool(query.filters["dangling"])
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "processing dangling filter"))
- return
+ idr := []types.ImageDeleteResponseItem{}
+ for k, v := range query.Filters {
+ for _, val := range v {
+ filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
- idr := []types.ImageDeleteResponseItem{}
- //
- // This code needs to be migrated to libpod to work correctly. I could not
- // work my around the information docker needs with the existing prune in libpod.
- //
- pruneImages, err := runtime.ImageRuntime().GetPruneImages(!dangling, []image2.ImageFilter{})
+ pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), false, filters)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to get images to prune"))
+ utils.InternalServerError(w, err)
return
}
- for _, p := range pruneImages {
- repotags, err := p.RepoTags()
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to get repotags for image"))
- return
- }
- if err := p.Remove(r.Context(), true); err != nil {
- if errors.Cause(err) == storage.ErrImageUsedByContainer {
- logrus.Warnf("Failed to prune image %s as it is in use: %v", p.ID(), err)
- continue
- }
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to prune image"))
- return
- }
- // newimageevent is not export therefore we cannot record the event. this will be fixed
- // when the prune is fixed in libpod
- // defer p.newImageEvent(events.Prune)
- response := types.ImageDeleteResponseItem{
- Deleted: fmt.Sprintf("sha256:%s", p.ID()), // I ack this is not ideal
- }
- if len(repotags) > 0 {
- response.Untagged = repotags[0]
- }
- idr = append(idr, response)
+ for _, p := range pruneCids {
+ idr = append(idr, types.ImageDeleteResponseItem{
+ Deleted: p,
+ })
}
+
+ //FIXME/TODO to do this exacty correct, pruneimages needs to return idrs and space-reclaimed, then we are golden
ipr := types.ImagesPruneReport{
ImagesDeleted: idr,
SpaceReclaimed: 1, // TODO we cannot supply this right now
@@ -343,8 +300,6 @@ func GetImage(w http.ResponseWriter, r *http.Request) {
}
func GetImages(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
images, err := utils.GetImages(w, r)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images"))
diff --git a/pkg/api/handlers/generic/ping.go b/pkg/api/handlers/generic/ping.go
index 44a67d53f..00afd86bc 100644
--- a/pkg/api/handlers/generic/ping.go
+++ b/pkg/api/handlers/generic/ping.go
@@ -3,6 +3,8 @@ package generic
import (
"fmt"
"net/http"
+
+ "github.com/containers/libpod/pkg/api/handlers"
)
func PingGET(w http.ResponseWriter, _ *http.Request) {
@@ -16,7 +18,7 @@ func PingHEAD(w http.ResponseWriter, _ *http.Request) {
}
func setHeaders(w http.ResponseWriter) {
- w.Header().Set("API-Version", DefaultApiVersion)
+ w.Header().Set("API-Version", handlers.DefaultApiVersion)
w.Header().Set("BuildKit-Version", "")
w.Header().Set("Docker-Experimental", "true")
w.Header().Set("Cache-Control", "no-cache")
diff --git a/pkg/api/handlers/generic/swagger.go b/pkg/api/handlers/generic/swagger.go
index 27e1fc18d..bfe527c41 100644
--- a/pkg/api/handlers/generic/swagger.go
+++ b/pkg/api/handlers/generic/swagger.go
@@ -1,11 +1,13 @@
package generic
+import "github.com/containers/libpod/pkg/api/handlers"
+
// Create container
// swagger:response ContainerCreateResponse
type swagCtrCreateResponse struct {
// in:body
Body struct {
- ContainerCreateResponse
+ handlers.ContainerCreateResponse
}
}
diff --git a/pkg/api/handlers/handler.go b/pkg/api/handlers/handler.go
index 4f303f6ab..d60a5b239 100644
--- a/pkg/api/handlers/handler.go
+++ b/pkg/api/handlers/handler.go
@@ -15,10 +15,11 @@ func getVar(r *http.Request, k string) string {
return mux.Vars(r)[k]
}
-func hasVar(r *http.Request, k string) bool {
- _, found := mux.Vars(r)[k]
- return found
-}
+// func hasVar(r *http.Request, k string) bool {
+// _, found := mux.Vars(r)[k]
+// return found
+// }
+
func getName(r *http.Request) string {
return getVar(r, "name")
}
diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go
index b4acdc312..3f66a63c8 100644
--- a/pkg/api/handlers/images.go
+++ b/pkg/api/handlers/images.go
@@ -3,7 +3,6 @@ package handlers
import (
"fmt"
"io"
- "io/ioutil"
"net/http"
"os"
"strconv"
@@ -127,46 +126,6 @@ func GetImage(r *http.Request, name string) (*image.Image, error) {
return runtime.ImageRuntime().NewFromLocal(name)
}
-func LoadImage(w http.ResponseWriter, r *http.Request) {
- decoder := r.Context().Value("decoder").(*schema.Decoder)
- runtime := r.Context().Value("runtime").(*libpod.Runtime)
-
- query := struct {
- //quiet bool # quiet is currently unused
- }{
- // This is where you can override the golang default value for one of fields
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
- return
- }
-
- var (
- err error
- writer io.Writer
- )
- f, err := ioutil.TempFile("", "api_load.tar")
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile"))
- return
- }
- if err := SaveFromBody(f, r); err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file"))
- return
- }
- id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "")
- if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image"))
- return
- }
- utils.WriteResponse(w, http.StatusOK, struct {
- Stream string `json:"stream"`
- }{
- Stream: fmt.Sprintf("Loaded image: %s\n", id),
- })
-}
-
func SaveFromBody(f *os.File, r *http.Request) error { // nolint
if _, err := io.Copy(f, r.Body); err != nil {
return err
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index df16843c7..db1cf26ff 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -19,8 +19,6 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
}
func ContainerExists(w http.ResponseWriter, r *http.Request) {
- // 404 no such container
- // 200 ok
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := mux.Vars(r)["name"]
_, err := runtime.LookupContainer(name)
@@ -147,10 +145,6 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
// tail string
}
-func CreateContainer(w http.ResponseWriter, r *http.Request) {
-
-}
-
func UnmountContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := mux.Vars(r)["name"]
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 0d4e220a8..9f0876618 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -1,9 +1,12 @@
package libpod
import (
+ "fmt"
+ "io"
"io/ioutil"
"net/http"
"os"
+ "strconv"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/api/handlers"
@@ -42,12 +45,12 @@ func ImageExists(w http.ResponseWriter, r *http.Request) {
func ImageTree(w http.ResponseWriter, r *http.Request) {
// tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework
- //name := mux.Vars(r)["name"]
- //_, layerInfoMap, _, err := s.Runtime.Tree(name)
- //if err != nil {
+ // name := mux.Vars(r)["name"]
+ // _, layerInfoMap, _, err := s.Runtime.Tree(name)
+ // if err != nil {
// Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name))
// return
- //}
+ // }
// it is not clear to me how to deal with this given all the processing of the image
// is in main. we need to discuss how that really should be and return something useful.
handlers.UnsupportedHandler(w, r)
@@ -90,13 +93,14 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
}
func PruneImages(w http.ResponseWriter, r *http.Request) {
- // 200 ok
- // 500 internal
+ var (
+ all bool
+ err error
+ )
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- All bool `schema:"all"`
- Filters []string `schema:"filters"`
+ Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
}
@@ -106,7 +110,19 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, query.Filters)
+
+ var libpodFilters = []string{}
+ if _, found := r.URL.Query()["filters"]; found {
+ all, err = strconv.ParseBool(query.Filters["all"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ for k, v := range query.Filters {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
+ }
+ }
+ cids, err := runtime.ImageRuntime().PruneImages(r.Context(), all, libpodFilters)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
@@ -163,3 +179,47 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
defer os.Remove(tmpfile.Name())
utils.WriteResponse(w, http.StatusOK, rdr)
}
+
+func ImportImage(w http.ResponseWriter, r *http.Request) {
+ // TODO this is basically wrong
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ query := struct {
+ Changes map[string]string `json:"changes"`
+ Message string `json:"message"`
+ Quiet bool `json:"quiet"`
+ }{
+ // This is where you can override the golang default value for one of fields
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ var (
+ err error
+ writer io.Writer
+ )
+ f, err := ioutil.TempFile("", "api_load.tar")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile"))
+ return
+ }
+ if err := handlers.SaveFromBody(f, r); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file"))
+ return
+ }
+ id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "")
+ //id, err := runtime.Import(r.Context())
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image"))
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, struct {
+ Stream string `json:"stream"`
+ }{
+ Stream: fmt.Sprintf("Loaded image: %s\n", id),
+ })
+}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 14f8e8de7..50d763338 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -108,7 +108,7 @@ func Pods(w http.ResponseWriter, r *http.Request) {
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- filters []string `schema:"filters"`
+ Filters map[string][]string `schema:"filters"`
}{
// override any golang type defaults
}
@@ -117,10 +117,12 @@ func Pods(w http.ResponseWriter, r *http.Request) {
errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- if len(query.filters) > 0 {
+
+ if _, found := r.URL.Query()["filters"]; found {
utils.Error(w, "filters are not implemented yet", http.StatusInternalServerError, define.ErrNotImplemented)
return
}
+
pods, err := runtime.GetAllPods()
if err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
@@ -164,7 +166,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
decoder = r.Context().Value("decoder").(*schema.Decoder)
)
query := struct {
- timeout int `schema:"t"`
+ Timeout int `schema:"t"`
}{
// override any golang type defaults
}
@@ -207,8 +209,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
return
}
- if query.timeout > 0 {
- _, stopError = pod.StopWithTimeout(r.Context(), false, query.timeout)
+ if query.Timeout > 0 {
+ _, stopError = pod.StopWithTimeout(r.Context(), false, query.Timeout)
} else {
_, stopError = pod.Stop(r.Context(), false)
}
@@ -266,7 +268,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
decoder = r.Context().Value("decoder").(*schema.Decoder)
)
query := struct {
- force bool `schema:"force"`
+ Force bool `schema:"force"`
}{
// override any golang type defaults
}
@@ -282,7 +284,7 @@ func PodDelete(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- if err := runtime.RemovePod(r.Context(), pod, true, query.force); err != nil {
+ if err := runtime.RemovePod(r.Context(), pod, true, query.Force); err != nil {
utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
return
}
@@ -307,43 +309,14 @@ func PodRestart(w http.ResponseWriter, r *http.Request) {
func PodPrune(w http.ResponseWriter, r *http.Request) {
var (
- err error
- pods []*libpod.Pod
runtime = r.Context().Value("runtime").(*libpod.Runtime)
- decoder = r.Context().Value("decoder").(*schema.Decoder)
)
- query := struct {
- force bool `schema:"force"`
- }{
- // override any golang type defaults
- }
-
- if err := decoder.Decode(&query, r.URL.Query()); err != nil {
- utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
- return
- }
-
- if query.force {
- pods, err = runtime.GetAllPods()
- if err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- } else {
- // TODO We need to make a libpod.PruneVolumes or this code will be a mess. Volumes
- // already does this right. It will also help clean this code path up with less
- // conditionals. We do this when we integrate with libpod again.
- utils.Error(w, "not implemented", http.StatusInternalServerError, errors.New("not implemented"))
+ pruned, err := runtime.PrunePods()
+ if err != nil {
+ utils.InternalServerError(w, err)
return
}
- for _, p := range pods {
- if err := runtime.RemovePod(r.Context(), p, true, query.force); err != nil {
- utils.Error(w, "Something went wrong", http.StatusInternalServerError, err)
- return
- }
- }
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusOK, pruned)
}
func PodPause(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 33cd51164..33cf1e95d 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -537,3 +537,11 @@ func portsToPortSet(input map[string]struct{}) (nat.PortSet, error) {
}
return ports, nil
}
+
+// ContainerCreateResponse is the response struct for creating a container
+type ContainerCreateResponse struct {
+ // ID of the container created
+ ID string `json:"id"`
+ // Warnings during container creation
+ Warnings []string `json:"Warnings"`
+}
diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go
index 9d2081cd8..8d499f40b 100644
--- a/pkg/api/handlers/utils/errors.go
+++ b/pkg/api/handlers/utils/errors.go
@@ -21,8 +21,9 @@ func Error(w http.ResponseWriter, apiMessage string, code int, err error) {
// Log detailed message of what happened to machine running podman service
log.Infof("Request Failed(%s): %s", http.StatusText(code), err.Error())
em := ErrorModel{
- Because: (errors.Cause(err)).Error(),
- Message: err.Error(),
+ Because: (errors.Cause(err)).Error(),
+ Message: err.Error(),
+ ResponseCode: code,
}
WriteJSON(w, code, em)
}
@@ -79,6 +80,8 @@ type ErrorModel struct {
// human error message, formatted for a human to read
// example: human error message
Message string `json:"message"`
+ // http response code
+ ResponseCode int `json:"response"`
}
func (e ErrorModel) Error() string {
@@ -89,6 +92,10 @@ func (e ErrorModel) Cause() error {
return errors.New(e.Because)
}
+func (e ErrorModel) Code() int {
+ return e.ResponseCode
+}
+
// UnsupportedParameter logs a given param by its string name as not supported.
func UnSupportedParameter(param string) {
log.Infof("API parameter %q: not supported", param)
diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go
index 9445298ca..2b651584a 100644
--- a/pkg/api/handlers/utils/images.go
+++ b/pkg/api/handlers/utils/images.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
+ "github.com/gorilla/mux"
"github.com/gorilla/schema"
)
@@ -15,17 +16,23 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
query := struct {
- //all bool # all is currently unused
- filters []string
- //digests bool # digests is currently unused
+ All bool
+ Filters map[string][]string `schema:"filters"`
+ Digests bool
}{
// This is where you can override the golang default value for one of fields
}
+ // TODO I think all is implemented with a filter?
+
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
return nil, err
}
- filters := query.filters
- if len(filters) < 1 {
+ var filters = []string{}
+ if _, found := mux.Vars(r)["digests"]; found && query.Digests {
+ UnSupportedParameter("digests")
+ }
+
+ if _, found := r.URL.Query()["filters"]; found {
filters = append(filters, fmt.Sprintf("reference=%s", ""))
}
return runtime.ImageRuntime().GetImagesWithFilters(filters)
diff --git a/pkg/api/handlers/generic/version.go b/pkg/api/handlers/version.go
index 39423914d..94166952c 100644
--- a/pkg/api/handlers/generic/version.go
+++ b/pkg/api/handlers/version.go
@@ -1,4 +1,4 @@
-package generic
+package handlers
import (
"fmt"
@@ -8,7 +8,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
docker "github.com/docker/docker/api/types"
"github.com/pkg/errors"
@@ -53,7 +52,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
},
}}
- utils.WriteResponse(w, http.StatusOK, handlers.Version{Version: docker.Version{
+ utils.WriteResponse(w, http.StatusOK, Version{Version: docker.Version{
Platform: struct {
Name string
}{
diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go
index e028c6302..c989c7927 100644
--- a/pkg/api/server/docs.go
+++ b/pkg/api/server/docs.go
@@ -12,7 +12,8 @@
// Version: 0.0.1
// License: Apache-2.0 https://opensource.org/licenses/Apache-2.0
// Contact: Podman <podman@lists.podman.io> https://podman.io/community/
-// Extensions:
+//
+// InfoExtensions:
// x-logo:
// - url: https://raw.githubusercontent.com/containers/libpod/master/logo/podman-logo.png
// - altText: "Podman logo"
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index b2d2ab388..abae81c38 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -33,7 +33,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
// $ref: "#/responses/ConflictError"
// 500:
// $ref: "#/responses/InternalError"
- r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, generic.CreateContainer)).Methods(http.MethodPost)
+ r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost)
// swagger:operation GET /containers/json compat listContainers
// ---
// tags:
@@ -550,7 +550,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error {
libpod endpoints
*/
- r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, libpod.CreateContainer)).Methods(http.MethodPost)
+ r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost)
// swagger:operation GET /libpod/containers/json libpod libpodListContainers
// ---
// tags:
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 6e8b79313..f1cc0574c 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -55,6 +55,27 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - images (compat)
// summary: List Images
// description: Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image.
+ // parameters:
+ // - name: all
+ // in: query
+ // description: "Show all images. Only images from a final layer (no children) are shown by default."
+ // type: boolean
+ // default: false
+ // - name: filters
+ // in: query
+ // description: |
+ // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters:
+ // - `before`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
+ // - `dangling=true`
+ // - `label=key` or `label="key=value"` of an image label
+ // - `reference`=(`<image-name>[:<tag>]`)
+ // - `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
+ // type: string
+ // - name: digests
+ // in: query
+ // description: Not supported
+ // type: boolean
+ // default: false
// produces:
// - application/json
// responses:
@@ -63,7 +84,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet)
- // swagger:operation POST /images/load compat loadImage
+ // swagger:operation POST /images/load compat importImage
// ---
// tags:
// - images (compat)
@@ -86,7 +107,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: no error
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, libpod.ImportImage)).Methods(http.MethodPost)
// swagger:operation POST /images/prune compat pruneImages
// ---
// tags:
@@ -174,7 +195,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: '#/responses/ConflictError'
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
+ r.Handle(VersionedPath("/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /images/{name}/get compat exportImage
// ---
// tags:
@@ -585,6 +606,22 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - images
// summary: List Images
// description: Returns a list of images on the server
+ // parameters:
+ // - name: all
+ // in: query
+ // description: Show all images. Only images from a final layer (no children) are shown by default.
+ // type: boolean
+ // default: false
+ // - name: filters
+ // in: query
+ // description: |
+ // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters:
+ // - `before`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
+ // - `dangling=true`
+ // - `label=key` or `label="key=value"` of an image label
+ // - `reference`=(`<image-name>[:<tag>]`)
+ // - `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
+ // type: string
// produces:
// - application/json
// responses:
@@ -593,7 +630,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/json"), APIHandler(s.Context, libpod.GetImages)).Methods(http.MethodGet)
- // swagger:operation POST /libpod/images/load libpod libpodLoadImage
+ // swagger:operation POST /libpod/images/load libpod libpodImportImage
// ---
// tags:
// - images
@@ -604,6 +641,14 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// name: quiet
// type: boolean
// description: not supported
+ // - in: query
+ // name: change
+ // description: "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR. JSON encoded string"
+ // type: string
+ // - in: query
+ // name: message
+ // description: Set commit message for imported image
+ // type: string
// - in: body
// name: request
// description: tarball of container image
@@ -617,7 +662,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: no error
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost)
+ r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, libpod.ImportImage)).Methods(http.MethodPost)
// swagger:operation POST /libpod/images/prune libpod libpodPruneImages
// ---
// tags:
@@ -635,10 +680,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// (or `0`), all unused images are pruned.
// - `until=<string>` Prune images created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time.
// - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune images with (or without, in case `label!=...` is used) the specified labels.
- // - in: query
- // name: all
- // type: boolean
- // description: prune all images
// produces:
// - application/json
// responses:
@@ -707,7 +748,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// $ref: '#/responses/ConflictError'
// 500:
// $ref: '#/responses/InternalError'
- r.Handle(VersionedPath("/libpod/images/name"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
+ r.Handle(VersionedPath("/libpod/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete)
// swagger:operation GET /libpod/images/{name}/get libpod libpoodExportImage
// ---
// tags:
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 1ef14b58c..5c7b51871 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -30,17 +30,15 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// swagger:operation POST /libpod/pods/prune pods PrunePods
// ---
// summary: Prune unused pods
- // parameters:
- // - in: query
- // name: force
- // description: force delete
- // type: boolean
- // default: false
// produces:
// - application/json
// responses:
- // 204:
- // description: no error
+ // 200:
+ // description: tbd
+ // schema:
+ // type: object
+ // additionalProperties:
+ // type: string
// 400:
// $ref: "#/responses/BadParamError"
// 500:
@@ -60,7 +58,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// - in: query
// name: force
// type: boolean
- // description: force delete
+ // description : force removal of a running pod by first stopping all containers, then removing all containers in the pod
// responses:
// 204:
// description: no error
diff --git a/pkg/api/server/register_version.go b/pkg/api/server/register_version.go
index 94216b1b6..d3b47c2a9 100644
--- a/pkg/api/server/register_version.go
+++ b/pkg/api/server/register_version.go
@@ -1,12 +1,12 @@
package server
import (
- "github.com/containers/libpod/pkg/api/handlers/generic"
+ "github.com/containers/libpod/pkg/api/handlers"
"github.com/gorilla/mux"
)
func (s *APIServer) registerVersionHandlers(r *mux.Router) error {
- r.Handle("/version", APIHandler(s.Context, generic.VersionHandler))
- r.Handle(VersionedPath("/version"), APIHandler(s.Context, generic.VersionHandler))
+ r.Handle("/version", APIHandler(s.Context, handlers.VersionHandler))
+ r.Handle(VersionedPath("/version"), APIHandler(s.Context, handlers.VersionHandler))
return nil
}
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index 847d11c3c..8c940763e 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -11,6 +11,7 @@ import (
"time"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
"github.com/coreos/go-systemd/activation"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
@@ -71,7 +72,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
ReadTimeout: 20 * time.Second,
WriteTimeout: 2 * time.Minute,
},
- Decoder: schema.NewDecoder(),
+ Decoder: handlers.NewAPIDecoder(),
Context: nil,
Runtime: runtime,
Listener: *listener,
@@ -85,6 +86,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
})
ctx, cancelFn := context.WithCancel(context.Background())
+ server.CancelFunc = cancelFn
// TODO: Use ConnContext when ported to go 1.13
ctx = context.WithValue(ctx, "decoder", server.Decoder)
@@ -92,9 +94,6 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
ctx = context.WithValue(ctx, "shutdownFunc", server.Shutdown)
server.Context = ctx
- server.CancelFunc = cancelFn
- server.Decoder.IgnoreUnknownKeys(true)
-
router.NotFoundHandler = http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
// We can track user errors...
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 551a63c62..3dec6ca20 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -1,14 +1,22 @@
package bindings
import (
+ "context"
"fmt"
"io"
+ "net"
"net/http"
+ "net/url"
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/libpod/pkg/api/handlers"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/pkg/errors"
)
-const (
- defaultConnection string = "http://localhost:8080/v1.24/libpod"
- pingConnection string = "http://localhost:8080/_ping"
+var (
+ defaultConnectionPath string = filepath.Join(fmt.Sprintf("v%s", handlers.MinimalApiVersion), "libpod")
)
type APIResponse struct {
@@ -17,46 +25,170 @@ type APIResponse struct {
}
type Connection struct {
- url string
- client *http.Client
+ scheme string
+ address string
+ client *http.Client
}
-func NewConnection(url string) (Connection, error) {
- if len(url) < 1 {
- url = defaultConnection
+// NewConnection takes a URI as a string and returns a context with the
+// Connection embedded as a value. This context needs to be passed to each
+// endpoint to work correctly.
+//
+// A valid URI connection should be scheme://
+// For example tcp://localhost:<port>
+// or unix://run/podman/podman.sock
+func NewConnection(uri string) (context.Context, error) {
+ u, err := url.Parse(uri)
+ if err != nil {
+ return nil, err
}
- newConn := Connection{
- url: url,
- client: &http.Client{},
+ // TODO once ssh is implemented, remove this block and
+ // add it to the conditional beneath it
+ if u.Scheme == "ssh" {
+ return nil, ErrNotImplemented
+ }
+ if u.Scheme != "tcp" && u.Scheme != "unix" {
+ return nil, errors.Errorf("%s is not a support schema", u.Scheme)
+ }
+
+ if u.Scheme == "tcp" && !strings.HasPrefix(uri, "tcp://") {
+ return nil, errors.New("tcp URIs should begin with tcp://")
+ }
+
+ address := u.Path
+ if u.Scheme == "tcp" {
+ address = u.Host
+ }
+ newConn := newConnection(u.Scheme, address)
+ ctx := context.WithValue(context.Background(), "conn", &newConn)
+ if err := pingNewConnection(ctx); err != nil {
+ return nil, err
}
- response, err := http.Get(pingConnection)
+ return ctx, nil
+}
+
+// pingNewConnection pings to make sure the RESTFUL service is up
+// and running. it should only be used where initializing a connection
+func pingNewConnection(ctx context.Context) error {
+ conn, err := GetConnectionFromContext(ctx)
if err != nil {
- return newConn, err
+ return err
+ }
+ // the ping endpoint sits at / in this case
+ response, err := conn.DoRequest(nil, http.MethodGet, "../../../_ping", nil)
+ if err != nil {
+ return err
+ }
+ if response.StatusCode == http.StatusOK {
+ return nil
}
- if err := response.Body.Close(); err != nil {
- return newConn, err
+ return errors.Errorf("ping response was %q", response.StatusCode)
+}
+
+// newConnection takes a scheme and address and creates a connection from it
+func newConnection(scheme, address string) Connection {
+ client := http.Client{
+ Transport: &http.Transport{
+ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
+ return net.Dial(scheme, address)
+ },
+ },
}
- return newConn, err
+ newConn := Connection{
+ client: &client,
+ address: address,
+ scheme: scheme,
+ }
+ return newConn
}
-func (c Connection) makeEndpoint(u string) string {
- return fmt.Sprintf("%s%s", defaultConnection, u)
+func (c *Connection) makeEndpoint(u string) string {
+ // The d character in the url is discarded and is meaningless
+ return fmt.Sprintf("http://d/%s%s", defaultConnectionPath, u)
}
-func (c Connection) newRequest(httpMethod, endpoint string, httpBody io.Reader, params map[string]string) (*APIResponse, error) {
- e := c.makeEndpoint(endpoint)
+// DoRequest assembles the http request and returns the response
+func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams map[string]string, pathValues ...string) (*APIResponse, error) {
+ var (
+ err error
+ response *http.Response
+ )
+ safePathValues := make([]interface{}, len(pathValues))
+ // Make sure path values are http url safe
+ for _, pv := range pathValues {
+ safePathValues = append(safePathValues, url.QueryEscape(pv))
+ }
+ safeEndpoint := fmt.Sprintf(endpoint, safePathValues...)
+
+ e := c.makeEndpoint(safeEndpoint)
req, err := http.NewRequest(httpMethod, e, httpBody)
if err != nil {
return nil, err
}
- if len(params) > 0 {
+ if len(queryParams) > 0 {
// if more desirable we could use url to form the encoded endpoint with params
r := req.URL.Query()
- for k, v := range params {
- r.Add(k, v)
+ for k, v := range queryParams {
+ r.Add(k, url.QueryEscape(v))
}
req.URL.RawQuery = r.Encode()
}
- response, err := c.client.Do(req) // nolint
+ // Give the Do three chances in the case of a comm/service hiccup
+ for i := 0; i < 3; i++ {
+ response, err = c.client.Do(req) // nolint
+ if err == nil {
+ break
+ }
+ }
return &APIResponse{response, req}, err
}
+
+// GetConnectionFromContext returns a bindings connection from the context
+// being passed into each method.
+func GetConnectionFromContext(ctx context.Context) (*Connection, error) {
+ c := ctx.Value("conn")
+ if c == nil {
+ return nil, errors.New("unable to get connection from context")
+ }
+ conn := c.(Connection)
+ return &conn, nil
+}
+
+// FiltersToHTML converts our typical filter format of a
+// map[string][]string to a query/html safe string.
+func FiltersToHTML(filters map[string][]string) (string, error) {
+ lowerCaseKeys := make(map[string][]string)
+ for k, v := range filters {
+ lowerCaseKeys[strings.ToLower(k)] = v
+ }
+ unsafeString, err := jsoniter.MarshalToString(lowerCaseKeys)
+ if err != nil {
+ return "", err
+ }
+ return url.QueryEscape(unsafeString), nil
+}
+
+// IsInformation returns true if the response code is 1xx
+func (h *APIResponse) IsInformational() bool {
+ return h.Response.StatusCode/100 == 1
+}
+
+// IsSuccess returns true if the response code is 2xx
+func (h *APIResponse) IsSuccess() bool {
+ return h.Response.StatusCode/100 == 2
+}
+
+// IsRedirection returns true if the response code is 3xx
+func (h *APIResponse) IsRedirection() bool {
+ return h.Response.StatusCode/100 == 3
+}
+
+// IsClientError returns true if the response code is 4xx
+func (h *APIResponse) IsClientError() bool {
+ return h.Response.StatusCode/100 == 4
+}
+
+// IsServerError returns true if the response code is 5xx
+func (h *APIResponse) IsServerError() bool {
+ return h.Response.StatusCode/100 == 5
+}
diff --git a/pkg/bindings/containers.go b/pkg/bindings/containers.go
deleted file mode 100644
index 057580088..000000000
--- a/pkg/bindings/containers.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
- "strconv"
-
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod"
-)
-
-func (c Connection) ListContainers(filter []string, last int, size, sync bool) ([]shared.PsContainerOutput, error) { // nolint:typecheck
- images := []shared.PsContainerOutput{}
- params := make(map[string]string)
- params["last"] = strconv.Itoa(last)
- params["size"] = strconv.FormatBool(size)
- params["sync"] = strconv.FormatBool(sync)
- response, err := c.newRequest(http.MethodGet, "/containers/json", nil, params)
- if err != nil {
- return images, err
- }
- return images, response.Process(nil)
-}
-
-func (c Connection) PruneContainers() ([]string, error) {
- var (
- pruned []string
- )
- response, err := c.newRequest(http.MethodPost, "/containers/prune", nil, nil)
- if err != nil {
- return pruned, err
- }
- return pruned, response.Process(nil)
-}
-
-func (c Connection) RemoveContainer(nameOrID string, force, volumes bool) error {
- params := make(map[string]string)
- params["force"] = strconv.FormatBool(force)
- params["vols"] = strconv.FormatBool(volumes)
- response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/containers/%s", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) InspectContainer(nameOrID string, size bool) (*libpod.InspectContainerData, error) {
- params := make(map[string]string)
- params["size"] = strconv.FormatBool(size)
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/containers/%s/json", nameOrID), nil, params)
- if err != nil {
- return nil, err
- }
- inspect := libpod.InspectContainerData{}
- return &inspect, response.Process(&inspect)
-}
-
-func (c Connection) KillContainer(nameOrID string, signal int) error {
- params := make(map[string]string)
- params["signal"] = strconv.Itoa(signal)
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/kill", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-
-}
-func (c Connection) ContainerLogs() {}
-func (c Connection) PauseContainer(nameOrID string) error {
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/pause", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) RestartContainer(nameOrID string, timeout int) error {
- // TODO how do we distinguish between an actual zero value and not wanting to change the timeout value
- params := make(map[string]string)
- params["timeout"] = strconv.Itoa(timeout)
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/restart", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) StartContainer(nameOrID, detachKeys string) error {
- params := make(map[string]string)
- if len(detachKeys) > 0 {
- params["detachKeys"] = detachKeys
- }
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/start", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) ContainerStats() {}
-func (c Connection) ContainerTop() {}
-
-func (c Connection) UnpauseContainer(nameOrID string) error {
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/unpause", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) WaitContainer(nameOrID string) error {
- // TODO when returns are ironed out, we can should use the newRequest approach
- _, err := http.Post(c.makeEndpoint(fmt.Sprintf("containers/%s/wait", nameOrID)), "application/json", nil) // nolint
- return err
-}
-
-func (c Connection) ContainerExists(nameOrID string) (bool, error) {
- response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/containers/%s/exists", nameOrID))) // nolint
- defer closeResponseBody(response)
- if err != nil {
- return false, err
- }
- if response.StatusCode == http.StatusOK {
- return true, nil
- }
- return false, nil
-}
-
-func (c Connection) StopContainer(nameOrID string, timeout *int) error {
- params := make(map[string]string)
- if timeout != nil {
- params["t"] = strconv.Itoa(*timeout)
- }
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/stop", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
new file mode 100644
index 000000000..334a656d4
--- /dev/null
+++ b/pkg/bindings/containers/containers.go
@@ -0,0 +1,255 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+ "strconv"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+// List obtains a list of containers in local storage. All parameters to this method are optional.
+// The filters are used to determine which containers are listed. The last parameter indicates to only return
+// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
+// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
+// container state.
+func List(ctx context.Context, filters map[string][]string, last *int, pod, size, sync *bool) ([]*shared.PsContainerOutput, error) { // nolint:typecheck
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ var images []*shared.PsContainerOutput
+ params := make(map[string]string)
+ if last != nil {
+ params["last"] = strconv.Itoa(*last)
+ }
+ if pod != nil {
+ params["pod"] = strconv.FormatBool(*pod)
+ }
+ if size != nil {
+ params["size"] = strconv.FormatBool(*size)
+ }
+ if sync != nil {
+ params["sync"] = strconv.FormatBool(*sync)
+ }
+ if filters != nil {
+ filterString, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = filterString
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params)
+ if err != nil {
+ return images, err
+ }
+ return images, response.Process(nil)
+}
+
+// Prune removes stopped and exited containers from local storage. The optional filters can be
+// used for more granular selection of containers. The main error returned indicates if there were runtime
+// errors like finding containers. Errors specific to the removal of a container are in the PruneContainerResponse
+// structure.
+func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
+ var (
+ pruneResponse []string
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if filters != nil {
+ filterString, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = filterString
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params)
+ if err != nil {
+ return pruneResponse, err
+ }
+ return pruneResponse, response.Process(pruneResponse)
+}
+
+// Remove removes a container from local storage. The force bool designates
+// that the container should be removed forcibly (example, even it is running). The volumes
+// bool dictates that a container's volumes should also be removed.
+func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if force != nil {
+ params["force"] = strconv.FormatBool(*force)
+ }
+ if volumes != nil {
+ params["vols"] = strconv.FormatBool(*volumes)
+ }
+ response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Inspect returns low level information about a Container. The nameOrID can be a container name
+// or a partial/full ID. The size bool determines whether the size of the container's root filesystem
+// should be calculated. Calculating the size of a container requires extra work from the filesystem and
+// is therefore slower.
+func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectContainerData, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if size != nil {
+ params["size"] = strconv.FormatBool(*size)
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ inspect := libpod.InspectContainerData{}
+ return &inspect, response.Process(&inspect)
+}
+
+// Kill sends a given signal to a given container. The signal should be the string
+// representation of a signal like 'SIGKILL'. The nameOrID can be a container name
+// or a partial/full ID
+func Kill(ctx context.Context, nameOrID string, signal string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ params["signal"] = signal
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+
+}
+func Logs() {}
+
+// Pause pauses a given container. The nameOrID can be a container name
+// or a partial/full ID.
+func Pause(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/pause", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Restart restarts a running container. The nameOrID can be a container name
+// or a partial/full ID. The optional timeout specifies the number of seconds to wait
+// for the running container to stop before killing it.
+func Restart(ctx context.Context, nameOrID string, timeout *int) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if timeout != nil {
+ params["t"] = strconv.Itoa(*timeout)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Start starts a non-running container.The nameOrID can be a container name
+// or a partial/full ID. The optional parameter for detach keys are to override the default
+// detach key sequence.
+func Start(ctx context.Context, nameOrID string, detachKeys *string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if detachKeys != nil {
+ params["detachKeys"] = *detachKeys
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+func Stats() {}
+func Top() {}
+
+// Unpause resumes the given paused container. The nameOrID can be a container name
+// or a partial/full ID.
+func Unpause(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unpause", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Wait blocks until the given container exits and returns its exit code. The nameOrID can be a container name
+// or a partial/full ID.
+func Wait(ctx context.Context, nameOrID string) (int32, error) {
+ var exitCode int32
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return exitCode, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "containers/%s/wait", nil, nameOrID)
+ if err != nil {
+ return exitCode, err
+ }
+ return exitCode, response.Process(&exitCode)
+}
+
+// Exists is a quick, light-weight way to determine if a given container
+// exists in local storage. The nameOrID can be a container name
+// or a partial/full ID.
+func Exists(ctx context.Context, nameOrID string) (bool, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return false, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "containers/%s/exists", nil, nameOrID)
+ if err != nil {
+ return false, err
+ }
+ return response.IsSuccess(), nil
+}
+
+// Stop stops a running container. The timeout is optional. The nameOrID can be a container name
+// or a partial/full ID
+func Stop(ctx context.Context, nameOrID string, timeout *int) error {
+ params := make(map[string]string)
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ if timeout != nil {
+ params["t"] = strconv.Itoa(*timeout)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go
new file mode 100644
index 000000000..9ed7f858d
--- /dev/null
+++ b/pkg/bindings/containers/healthcheck.go
@@ -0,0 +1,26 @@
+package containers
+
+import (
+ "context"
+ "github.com/containers/libpod/pkg/bindings"
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+)
+
+// RunHealthCheck executes the container's healthcheck and returns the health status of the
+// container.
+func RunHealthCheck(ctx context.Context, nameOrID string) (*libpod.HealthCheckStatus, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ status libpod.HealthCheckStatus
+ )
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/runhealthcheck", nil, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ return &status, response.Process(&status)
+}
diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go
new file mode 100644
index 000000000..d68dee981
--- /dev/null
+++ b/pkg/bindings/containers/mount.go
@@ -0,0 +1,53 @@
+package containers
+
+import (
+ "context"
+ "net/http"
+
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+// Mount mounts an existing container to the filesystem. It returns the path
+// of the mounted container in string format.
+func Mount(ctx context.Context, nameOrID string) (string, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return "", err
+ }
+ var (
+ path string
+ )
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/mount", nil, nameOrID)
+ if err != nil {
+ return path, err
+ }
+ return path, response.Process(&path)
+}
+
+// Unmount unmounts a container from the filesystem. The container must not be running
+// or the unmount will fail.
+func Unmount(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/unmount", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// GetMountedContainerPaths returns a map of mounted containers and their mount locations.
+func GetMountedContainerPaths(ctx context.Context) (map[string]string, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ mounts := make(map[string]string)
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/showmounted", nil)
+ if err != nil {
+ return mounts, err
+ }
+ return mounts, response.Process(&mounts)
+}
diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go
index 9a02925a3..8bd40f804 100644
--- a/pkg/bindings/errors.go
+++ b/pkg/bindings/errors.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
var (
@@ -37,10 +36,10 @@ func (a APIResponse) Process(unmarshalInto interface{}) error {
return handleError(data)
}
-func closeResponseBody(r *http.Response) {
- if r != nil {
- if err := r.Body.Close(); err != nil {
- logrus.Error(errors.Wrap(err, "unable to close response body"))
- }
+func CheckResponseCode(inError error) (int, error) {
+ e, ok := inError.(utils.ErrorModel)
+ if !ok {
+ return -1, errors.New("error is not type ErrorModel")
}
+ return e.Code(), nil
}
diff --git a/pkg/bindings/generate.go b/pkg/bindings/generate.go
deleted file mode 100644
index 534909062..000000000
--- a/pkg/bindings/generate.go
+++ /dev/null
@@ -1,4 +0,0 @@
-package bindings
-
-func (c Connection) GenerateKube() {}
-func (c Connection) GenerateSystemd() {}
diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go
new file mode 100644
index 000000000..2916754b8
--- /dev/null
+++ b/pkg/bindings/generate/generate.go
@@ -0,0 +1,4 @@
+package generate
+
+func GenerateKube() {}
+func GenerateSystemd() {}
diff --git a/pkg/bindings/healthcheck.go b/pkg/bindings/healthcheck.go
deleted file mode 100644
index 32515e332..000000000
--- a/pkg/bindings/healthcheck.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
-
- "github.com/containers/libpod/libpod"
-)
-
-func (c Connection) RunHealthCheck(nameOrID string) (*libpod.HealthCheckStatus, error) {
- var (
- status libpod.HealthCheckStatus
- )
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/containers/%s/runhealthcheck", nameOrID), nil, nil)
- if err != nil {
- return nil, err
- }
- return &status, response.Process(&status)
-}
diff --git a/pkg/bindings/images.go b/pkg/bindings/images.go
deleted file mode 100644
index 3abc8c372..000000000
--- a/pkg/bindings/images.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "io"
- "net/http"
- "strconv"
-
- "github.com/containers/libpod/pkg/api/handlers"
- "github.com/containers/libpod/pkg/inspect"
-)
-
-func (c Connection) ImageExists(nameOrID string) (bool, error) {
- response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/images/%s/exists", nameOrID))) // nolint
- defer closeResponseBody(response)
- if err != nil {
- return false, err
- }
- if response.StatusCode == http.StatusOK {
- return true, nil
- }
- return false, nil
-}
-
-func (c Connection) ListImages() ([]handlers.ImageSummary, error) {
- imageSummary := []handlers.ImageSummary{}
- response, err := c.newRequest(http.MethodGet, "/images/json", nil, nil)
- if err != nil {
- return imageSummary, err
- }
- return imageSummary, response.Process(&imageSummary)
-}
-
-func (c Connection) GetImage(nameOrID string) (*inspect.ImageData, error) {
- inspectedData := inspect.ImageData{}
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/json", nameOrID), nil, nil)
- if err != nil {
- return &inspectedData, err
- }
- return &inspectedData, response.Process(&inspectedData)
-}
-
-func (c Connection) ImageTree(nameOrId string) error {
- return ErrNotImplemented
-}
-
-func (c Connection) ImageHistory(nameOrID string) ([]handlers.HistoryResponse, error) {
- history := []handlers.HistoryResponse{}
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/history", nameOrID), nil, nil)
- if err != nil {
- return history, err
- }
- return history, response.Process(&history)
-}
-
-func (c Connection) LoadImage(r io.Reader) error {
- // TODO this still needs error handling added
- _, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint
- return err
-}
-
-func (c Connection) RemoveImage(nameOrID string, force bool) ([]map[string]string, error) {
- deletes := []map[string]string{}
- params := make(map[string]string)
- params["force"] = strconv.FormatBool(force)
- response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/images/%s", nameOrID), nil, params)
- if err != nil {
- return nil, err
- }
- return deletes, response.Process(&deletes)
-}
-
-func (c Connection) ExportImage(nameOrID string, w io.Writer, format string, compress bool) error {
- params := make(map[string]string)
- params["format"] = format
- params["compress"] = strconv.FormatBool(compress)
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/get", nameOrID), nil, params)
- if err != nil {
- return err
- }
- if err := response.Process(nil); err != nil {
- return err
- }
- _, err = io.Copy(w, response.Body)
- return err
-}
-
-func (c Connection) PruneImages(all bool, filters []string) ([]string, error) {
- var (
- deleted []string
- )
- params := make(map[string]string)
- // FIXME How do we do []strings?
- //params["filters"] = format
- response, err := c.newRequest(http.MethodPost, "/images/prune", nil, params)
- if err != nil {
- return deleted, err
- }
- return deleted, response.Process(nil)
-}
-
-func (c Connection) TagImage(nameOrID string) error {
- var ()
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/images/%s/tag", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) BuildImage(nameOrId string) {}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
new file mode 100644
index 000000000..deaf93f0e
--- /dev/null
+++ b/pkg/bindings/images/images.go
@@ -0,0 +1,187 @@
+package images
+
+import (
+ "context"
+ "io"
+ "net/http"
+ "strconv"
+
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/inspect"
+)
+
+// Exists a lightweight way to determine if an image exists in local storage. It returns a
+// boolean response.
+func Exists(ctx context.Context, nameOrID string) (bool, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return false, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/exists", nil, nameOrID)
+ if err != nil {
+ return false, err
+ }
+ return response.IsSuccess(), nil
+}
+
+// List returns a list of images in local storage. The all boolean and filters parameters are optional
+// ways to alter the image query.
+func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handlers.ImageSummary, error) {
+ var imageSummary []*handlers.ImageSummary
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if all != nil {
+ params["all"] = strconv.FormatBool(*all)
+ }
+ if filters != nil {
+ strFilters, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = strFilters
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params)
+ if err != nil {
+ return imageSummary, err
+ }
+ return imageSummary, response.Process(&imageSummary)
+}
+
+// Get performs an image inspect. To have the on-disk size of the image calculated, you can
+// use the optional size parameter.
+func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageData, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if size != nil {
+ params["size"] = strconv.FormatBool(*size)
+ }
+ inspectedData := inspect.ImageData{}
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID)
+ if err != nil {
+ return &inspectedData, err
+ }
+ return &inspectedData, response.Process(&inspectedData)
+}
+
+func ImageTree(ctx context.Context, nameOrId string) error {
+ return bindings.ErrNotImplemented
+}
+
+// History returns the parent layers of an image.
+func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, error) {
+ var history []*handlers.HistoryResponse
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/history", nil, nameOrID)
+ if err != nil {
+ return history, err
+ }
+ return history, response.Process(&history)
+}
+
+func Load(ctx context.Context, r io.Reader) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ // TODO this still needs error handling added
+ //_, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint
+ _ = conn
+ return bindings.ErrNotImplemented
+}
+
+// Remove deletes an image from local storage. The optional force parameter will forcibly remove
+// the image by removing all all containers, including those that are Running, first.
+func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]string, error) {
+ var deletes []map[string]string
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if force != nil {
+ params["force"] = strconv.FormatBool(*force)
+ }
+ response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ return deletes, response.Process(&deletes)
+}
+
+// Export saves an image from local storage as a tarball or image archive. The optional format
+// parameter is used to change the format of the output.
+func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, compress *bool) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if format != nil {
+ params["format"] = *format
+ }
+ if compress != nil {
+ params["compress"] = strconv.FormatBool(*compress)
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ if err := response.Process(nil); err != nil {
+ return err
+ }
+ _, err = io.Copy(w, response.Body)
+ return err
+}
+
+// Prune removes unused images from local storage. The optional filters can be used to further
+// define which images should be pruned.
+func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
+ var (
+ deleted []string
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if filters != nil {
+ stringFilter, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = stringFilter
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params)
+ if err != nil {
+ return deleted, err
+ }
+ return deleted, response.Process(nil)
+}
+
+// Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required.
+func Tag(ctx context.Context, nameOrID, tag, repo string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ params["tag"] = tag
+ params["repo"] = repo
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+func Build(nameOrId string) {}
diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go
new file mode 100644
index 000000000..d98ddf18d
--- /dev/null
+++ b/pkg/bindings/images/search.go
@@ -0,0 +1,40 @@
+package images
+
+import (
+ "context"
+ "net/http"
+ "strconv"
+
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+// Search looks for the given image (term) in container image registries. The optional limit parameter sets
+// a maximum number of results returned. The optional filters parameter allow for more specific image
+// searches.
+func Search(ctx context.Context, term string, limit *int, filters map[string][]string) ([]image.SearchResult, error) {
+ var (
+ searchResults []image.SearchResult
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ params["term"] = term
+ if limit != nil {
+ params["limit"] = strconv.Itoa(*limit)
+ }
+ if filters != nil {
+ stringFilter, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = stringFilter
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params)
+ if err != nil {
+ return searchResults, nil
+ }
+ return searchResults, response.Process(&searchResults)
+}
diff --git a/pkg/bindings/mount.go b/pkg/bindings/mount.go
deleted file mode 100644
index 2e3d6d7f6..000000000
--- a/pkg/bindings/mount.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
-)
-
-func (c Connection) MountContainer(nameOrID string) (string, error) {
- var (
- path string
- )
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/containers/%s/mount", nameOrID), nil, nil)
- if err != nil {
- return path, err
- }
- return path, response.Process(&path)
-}
-
-func (c Connection) GetMountedContainerPaths() (map[string]string, error) {
- mounts := make(map[string]string)
- response, err := c.newRequest(http.MethodGet, "/containers/showmounted", nil, nil)
- if err != nil {
- return mounts, err
- }
- return mounts, response.Process(&mounts)
-}
diff --git a/pkg/bindings/network.go b/pkg/bindings/network.go
deleted file mode 100644
index 383615e5d..000000000
--- a/pkg/bindings/network.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
-
- "github.com/containernetworking/cni/libcni"
-)
-
-func (c Connection) CreateNetwork() {}
-func (c Connection) InspectNetwork(nameOrID string) (map[string]interface{}, error) {
- n := make(map[string]interface{})
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/networks/%s/json", nameOrID), nil, nil)
- if err != nil {
- return n, err
- }
- return n, response.Process(&n)
-}
-
-func (c Connection) RemoveNetwork(nameOrID string) error {
- response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/networks/%s", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) ListNetworks() ([]*libcni.NetworkConfigList, error) {
- var (
- netList []*libcni.NetworkConfigList
- )
- response, err := c.newRequest(http.MethodGet, "/networks/json", nil, nil)
- if err != nil {
- return netList, err
- }
- return netList, response.Process(&netList)
-}
diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go
new file mode 100644
index 000000000..97bbb8c42
--- /dev/null
+++ b/pkg/bindings/network/network.go
@@ -0,0 +1,50 @@
+package network
+
+import (
+ "context"
+ "net/http"
+
+ "github.com/containernetworking/cni/libcni"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+func Create() {}
+func Inspect(ctx context.Context, nameOrID string) (map[string]interface{}, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ n := make(map[string]interface{})
+ response, err := conn.DoRequest(nil, http.MethodGet, "/networks/%s/json", nil, nameOrID)
+ if err != nil {
+ return n, err
+ }
+ return n, response.Process(&n)
+}
+
+func Remove(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+func List(ctx context.Context) ([]*libcni.NetworkConfigList, error) {
+ var (
+ netList []*libcni.NetworkConfigList
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", nil)
+ if err != nil {
+ return netList, err
+ }
+ return netList, response.Process(&netList)
+}
diff --git a/pkg/bindings/play.go b/pkg/bindings/play.go
deleted file mode 100644
index a9dee82b1..000000000
--- a/pkg/bindings/play.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package bindings
-
-func (c Connection) PlayKube() {}
diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go
new file mode 100644
index 000000000..a6f03cad2
--- /dev/null
+++ b/pkg/bindings/play/play.go
@@ -0,0 +1,7 @@
+package play
+
+import "github.com/containers/libpod/pkg/bindings"
+
+func PlayKube() error {
+ return bindings.ErrNotImplemented
+}
diff --git a/pkg/bindings/pods.go b/pkg/bindings/pods.go
deleted file mode 100644
index 704d71477..000000000
--- a/pkg/bindings/pods.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
- "strconv"
-
- "github.com/containers/libpod/libpod"
-)
-
-func (c Connection) CreatePod() error {
- // TODO
- return ErrNotImplemented
-}
-
-func (c Connection) PodExists(nameOrID string) (bool, error) {
- response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/pods/%s/exists", nameOrID))) // nolint
- defer closeResponseBody(response)
- if err != nil {
- return false, err
- }
- return response.StatusCode == http.StatusOK, err
-}
-
-func (c Connection) InspectPod(nameOrID string) (*libpod.PodInspect, error) {
- inspect := libpod.PodInspect{}
- response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/pods/%s/json", nameOrID), nil, nil)
- if err != nil {
- return &inspect, err
- }
- return &inspect, response.Process(&inspect)
-}
-
-func (c Connection) KillPod(nameOrID string, signal int) error {
- params := make(map[string]string)
- params["signal"] = strconv.Itoa(signal)
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/kill", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) PausePod(nameOrID string) error {
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/pause", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) PrunePods(force bool) error {
- params := make(map[string]string)
- params["force"] = strconv.FormatBool(force)
- response, err := c.newRequest(http.MethodPost, "/pods/prune", nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) ListPods(filters []string) (*[]libpod.PodInspect, error) {
- var (
- inspect []libpod.PodInspect
- )
- params := make(map[string]string)
- // TODO I dont remember how to do this for []string{}
- // FIXME
- //params["filters"] = strconv.FormatBool(force)
- response, err := c.newRequest(http.MethodPost, "/pods/json", nil, params)
- if err != nil {
- return &inspect, err
- }
- return &inspect, response.Process(&inspect)
-}
-
-func (c Connection) RestartPod(nameOrID string) error {
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/restart", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) RemovePod(nameOrID string, force bool) error {
- params := make(map[string]string)
- params["force"] = strconv.FormatBool(force)
- response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/pods/%s", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) StartPod(nameOrID string) error {
- response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/pods/%s/start", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) PodStats() error {
- // TODO
- return ErrNotImplemented
-}
-
-func (c Connection) StopPod(nameOrID string, timeout int) error {
- params := make(map[string]string)
- params["t"] = strconv.Itoa(timeout)
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/stop", nameOrID), nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
-
-func (c Connection) PodTop() error {
- // TODO
- return ErrNotImplemented // nolint:typecheck
-}
-
-func (c Connection) UnpausePod(nameOrID string) error {
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/pods/%s/unpause", nameOrID), nil, nil)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
new file mode 100644
index 000000000..a6b74c21d
--- /dev/null
+++ b/pkg/bindings/pods/pods.go
@@ -0,0 +1,196 @@
+package pods
+
+import (
+ "context"
+ "net/http"
+ "strconv"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+func CreatePod() error {
+ // TODO
+ return bindings.ErrNotImplemented
+}
+
+// Exists is a lightweight method to determine if a pod exists in local storage
+func Exists(ctx context.Context, nameOrID string) (bool, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return false, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/exists", nil, nameOrID)
+ if err != nil {
+ return false, err
+ }
+ return response.IsSuccess(), nil
+}
+
+// Inspect returns low-level information about the given pod.
+func Inspect(ctx context.Context, nameOrID string) (*libpod.PodInspect, error) {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ inspect := libpod.PodInspect{}
+ response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/json", nil, nameOrID)
+ if err != nil {
+ return &inspect, err
+ }
+ return &inspect, response.Process(&inspect)
+}
+
+// Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter
+// can be used to override SIGTERM.
+func Kill(ctx context.Context, nameOrID string, signal *string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if signal != nil {
+ params["signal"] = *signal
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Pause pauses all running containers in a given pod.
+func Pause(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/pause", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Prune removes all non-running pods in local storage.
+func Prune(ctx context.Context) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/prune", nil)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// List returns all pods in local storage. The optional filters parameter can
+// be used to refine which pods should be listed.
+func List(ctx context.Context, filters map[string][]string) (*[]libpod.PodInspect, error) {
+ var (
+ inspect []libpod.PodInspect
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := make(map[string]string)
+ if filters != nil {
+ stringFilter, err := bindings.FiltersToHTML(filters)
+ if err != nil {
+ return nil, err
+ }
+ params["filters"] = stringFilter
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/json", params)
+ if err != nil {
+ return &inspect, err
+ }
+ return &inspect, response.Process(&inspect)
+}
+
+// Restart restarts all containers in a pod.
+func Restart(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/restart", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Remove deletes a Pod from from local storage. The optional force parameter denotes
+// that the Pod can be removed even if in a running state.
+func Remove(ctx context.Context, nameOrID string, force *bool) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if force != nil {
+ params["force"] = strconv.FormatBool(*force)
+ }
+ response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+// Start starts all containers in a pod.
+func Start(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s/start", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+func Stats() error {
+ // TODO
+ return bindings.ErrNotImplemented
+}
+
+// Stop stops all containers in a Pod. The optional timeout parameter can be
+// used to override the timeout before the container is killed.
+func Stop(ctx context.Context, nameOrID string, timeout *int) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if timeout != nil {
+ params["t"] = strconv.Itoa(*timeout)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
+func Top() error {
+ // TODO
+ return bindings.ErrNotImplemented // nolint:typecheck
+}
+
+// Unpause unpauses all paused containers in a Pod.
+func Unpause(ctx context.Context, nameOrID string) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/unpause", nil, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
diff --git a/pkg/bindings/search.go b/pkg/bindings/search.go
deleted file mode 100644
index 0f462357c..000000000
--- a/pkg/bindings/search.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package bindings
-
-import (
- "net/http"
- "strconv"
-
- "github.com/containers/libpod/libpod/image"
-)
-
-type ImageSearchFilters struct {
- Automated bool `json:"automated"`
- Official bool `json:"official"`
- Stars int `json:"stars"`
-}
-
-// TODO This method can be concluded when we determine how we want the filters to work on the
-// API end
-func (i *ImageSearchFilters) ToMapJSON() string {
- return ""
-}
-
-func (c Connection) SearchImages(term string, limit int, filters *ImageSearchFilters) ([]image.SearchResult, error) {
- var (
- searchResults []image.SearchResult
- )
- params := make(map[string]string)
- params["term"] = term
- if limit > 0 {
- params["limit"] = strconv.Itoa(limit)
- }
- if filters != nil {
- params["filters"] = filters.ToMapJSON()
- }
- response, err := c.newRequest(http.MethodGet, "/images/search", nil, params)
- if err != nil {
- return searchResults, nil
- }
- return searchResults, response.Process(&searchResults)
-}
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
new file mode 100644
index 000000000..4f2a98f2b
--- /dev/null
+++ b/pkg/bindings/test/common_test.go
@@ -0,0 +1,112 @@
+package test_bindings
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+
+ "github.com/onsi/ginkgo"
+ "github.com/onsi/gomega/gexec"
+ "github.com/pkg/errors"
+)
+
+const (
+ defaultPodmanBinaryLocation string = "/usr/bin/podman"
+)
+
+type bindingTest struct {
+ artifactDirPath string
+ imageCacheDir string
+ sock string
+ tempDirPath string
+ runRoot string
+ crioRoot string
+}
+
+func (b *bindingTest) runPodman(command []string) *gexec.Session {
+ var cmd []string
+ podmanBinary := defaultPodmanBinaryLocation
+ val, ok := os.LookupEnv("PODMAN_BINARY")
+ if ok {
+ podmanBinary = val
+ }
+ val, ok = os.LookupEnv("CGROUP_MANAGER")
+ if ok {
+ cmd = append(cmd, "--cgroup-manager", val)
+ }
+ val, ok = os.LookupEnv("CNI_CONFIG_DIR")
+ if ok {
+ cmd = append(cmd, "--cni-config-dir", val)
+ }
+ val, ok = os.LookupEnv("CONMON")
+ if ok {
+ cmd = append(cmd, "--conmon", val)
+ }
+ val, ok = os.LookupEnv("ROOT")
+ if ok {
+ cmd = append(cmd, "--root", val)
+ } else {
+ cmd = append(cmd, "--root", b.crioRoot)
+ }
+ val, ok = os.LookupEnv("OCI_RUNTIME")
+ if ok {
+ cmd = append(cmd, "--runtime", val)
+ }
+ val, ok = os.LookupEnv("RUNROOT")
+ if ok {
+ cmd = append(cmd, "--runroot", val)
+ } else {
+ cmd = append(cmd, "--runroot", b.runRoot)
+ }
+ val, ok = os.LookupEnv("STORAGE_DRIVER")
+ if ok {
+ cmd = append(cmd, "--storage-driver", val)
+ }
+ val, ok = os.LookupEnv("STORAGE_OPTIONS")
+ if ok {
+ cmd = append(cmd, "--storage", val)
+ }
+ cmd = append(cmd, command...)
+ c := exec.Command(podmanBinary, cmd...)
+ fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(cmd, " "))
+ session, err := gexec.Start(c, ginkgo.GinkgoWriter, ginkgo.GinkgoWriter)
+ if err != nil {
+ panic(errors.Errorf("unable to run podman command: %q", cmd))
+ }
+ return session
+}
+
+func newBindingTest() *bindingTest {
+ tmpPath, _ := createTempDirInTempDir()
+ b := bindingTest{
+ crioRoot: filepath.Join(tmpPath, "crio"),
+ runRoot: filepath.Join(tmpPath, "run"),
+ artifactDirPath: "",
+ imageCacheDir: "",
+ sock: fmt.Sprintf("unix:%s", filepath.Join(tmpPath, "api.sock")),
+ tempDirPath: tmpPath,
+ }
+ return &b
+}
+
+// createTempDirinTempDir create a temp dir with prefix podman_test
+func createTempDirInTempDir() (string, error) {
+ return ioutil.TempDir("", "libpod_api")
+}
+
+func (b *bindingTest) startAPIService() *gexec.Session {
+ var (
+ cmd []string
+ )
+ cmd = append(cmd, "--log-level=debug", "service", "--timeout=999999", b.sock)
+ return b.runPodman(cmd)
+}
+
+func (b *bindingTest) cleanup() {
+ if err := os.RemoveAll(b.tempDirPath); err != nil {
+ fmt.Println(err)
+ }
+}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
new file mode 100644
index 000000000..d600197bb
--- /dev/null
+++ b/pkg/bindings/test/images_test.go
@@ -0,0 +1,92 @@
+package test_bindings
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/bindings/images"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman images", func() {
+ var (
+ //tempdir string
+ //err error
+ //podmanTest *PodmanTestIntegration
+ bt *bindingTest
+ s *gexec.Session
+ connText context.Context
+ err error
+ false bool
+ //true bool = true
+ )
+
+ BeforeEach(func() {
+ //tempdir, err = CreateTempDirInTempDir()
+ //if err != nil {
+ // os.Exit(1)
+ //}
+ //podmanTest = PodmanTestCreate(tempdir)
+ //podmanTest.Setup()
+ //podmanTest.SeedImages()
+ bt = newBindingTest()
+ p := bt.runPodman([]string{"pull", "docker.io/library/alpine:latest"})
+ p.Wait(45)
+ s = bt.startAPIService()
+ time.Sleep(1 * time.Second)
+ connText, err = bindings.NewConnection(bt.sock)
+ Expect(err).To(BeNil())
+ })
+
+ AfterEach(func() {
+ //podmanTest.Cleanup()
+ //f := CurrentGinkgoTestDescription()
+ //processTestResult(f)
+ s.Kill()
+ bt.cleanup()
+ })
+ It("inspect image", func() {
+ // Inspect invalid image be 404
+ _, err = images.GetImage(connText, "foobar5000", nil)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", 404))
+
+ // Inspect by short name
+ data, err := images.GetImage(connText, "alpine", nil)
+ Expect(err).To(BeNil())
+
+ // Inspect with full ID
+ _, err = images.GetImage(connText, data.ID, nil)
+ Expect(err).To(BeNil())
+
+ // Inspect with partial ID
+ _, err = images.GetImage(connText, data.ID[0:12], nil)
+ Expect(err).To(BeNil())
+ // Inspect by ID
+ // Inspect by long name should work, it doesnt (yet) i think it needs to be html escaped
+ //_, err = images.GetImage(connText, )
+ //Expect(err).To(BeNil())
+ })
+ It("remove image", func() {
+ // Remove invalid image should be a 404
+ _, err = images.RemoveImage(connText, "foobar5000", &false)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", 404))
+
+ _, err := images.GetImage(connText, "alpine", nil)
+ Expect(err).To(BeNil())
+
+ response, err := images.RemoveImage(connText, "alpine", &false)
+ Expect(err).To(BeNil())
+ fmt.Println(response)
+ // to be continued
+
+ })
+
+})
diff --git a/pkg/bindings/volumes.go b/pkg/bindings/volumes.go
deleted file mode 100644
index 219f924e7..000000000
--- a/pkg/bindings/volumes.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package bindings
-
-import (
- "fmt"
- "net/http"
- "strconv"
-
- "github.com/containers/libpod/libpod"
- "github.com/containers/libpod/pkg/api/handlers"
-)
-
-func (c Connection) CreateVolume(config handlers.VolumeCreateConfig) (string, error) {
- var (
- volumeID string
- )
- response, err := c.newRequest(http.MethodPost, "/volumes/create", nil, nil)
- if err != nil {
- return volumeID, err
- }
- return volumeID, response.Process(&volumeID)
-}
-
-func (c Connection) InspectVolume(nameOrID string) (*libpod.InspectVolumeData, error) {
- var (
- inspect libpod.InspectVolumeData
- )
- response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/volumes/%s/json", nameOrID), nil, nil)
- if err != nil {
- return &inspect, err
- }
- return &inspect, response.Process(&inspect)
-}
-
-func (c Connection) ListVolumes() error {
- // TODO
- // The API side of things for this one does a lot in main and therefore
- // is not implemented yet.
- return ErrNotImplemented // nolint:typecheck
-}
-
-func (c Connection) PruneVolumes() ([]string, error) {
- var (
- pruned []string
- )
- response, err := c.newRequest(http.MethodPost, "/volumes/prune", nil, nil)
- if err != nil {
- return pruned, err
- }
- return pruned, response.Process(&pruned)
-}
-
-func (c Connection) RemoveVolume(nameOrID string, force bool) error {
- params := make(map[string]string)
- params["force"] = strconv.FormatBool(force)
- response, err := c.newRequest(http.MethodPost, "/volumes/prune", nil, params)
- if err != nil {
- return err
- }
- return response.Process(nil)
-}
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
new file mode 100644
index 000000000..05a4f73fd
--- /dev/null
+++ b/pkg/bindings/volumes/volumes.go
@@ -0,0 +1,85 @@
+package volumes
+
+import (
+ "context"
+ "net/http"
+ "strconv"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+// Create creates a volume given its configuration.
+func Create(ctx context.Context, config handlers.VolumeCreateConfig) (string, error) {
+ // TODO This is incomplete. The config needs to be sent via the body
+ var (
+ volumeID string
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return "", err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/create", nil)
+ if err != nil {
+ return volumeID, err
+ }
+ return volumeID, response.Process(&volumeID)
+}
+
+// Inspect returns low-level information about a volume.
+func Inspect(ctx context.Context, nameOrID string) (*libpod.InspectVolumeData, error) {
+ var (
+ inspect libpod.InspectVolumeData
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/json", nil, nameOrID)
+ if err != nil {
+ return &inspect, err
+ }
+ return &inspect, response.Process(&inspect)
+}
+
+func List() error {
+ // TODO
+ // The API side of things for this one does a lot in main and therefore
+ // is not implemented yet.
+ return bindings.ErrNotImplemented // nolint:typecheck
+}
+
+// Prune removes unused volumes from the local filesystem.
+func Prune(ctx context.Context) ([]string, error) {
+ var (
+ pruned []string
+ )
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil)
+ if err != nil {
+ return pruned, err
+ }
+ return pruned, response.Process(&pruned)
+}
+
+// Remove deletes the given volume from storage. The optional force parameter
+// is used to remove a volume even if it is being used by a container.
+func Remove(ctx context.Context, nameOrID string, force *bool) error {
+ conn, err := bindings.GetConnectionFromContext(ctx)
+ if err != nil {
+ return err
+ }
+ params := make(map[string]string)
+ if force != nil {
+ params["force"] = strconv.FormatBool(*force)
+ }
+ response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/prune", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(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/vendor/github.com/fatih/camelcase/LICENSE.md b/pkg/util/camelcase/LICENSE.md
index aa4a536ca..aa4a536ca 100644
--- a/vendor/github.com/fatih/camelcase/LICENSE.md
+++ b/pkg/util/camelcase/LICENSE.md
diff --git a/vendor/github.com/fatih/camelcase/README.md b/pkg/util/camelcase/README.md
index 105a6ae33..105a6ae33 100644
--- a/vendor/github.com/fatih/camelcase/README.md
+++ b/pkg/util/camelcase/README.md
diff --git a/vendor/github.com/fatih/camelcase/camelcase.go b/pkg/util/camelcase/camelcase.go
index 02160c9a4..0a82d1005 100644
--- a/vendor/github.com/fatih/camelcase/camelcase.go
+++ b/pkg/util/camelcase/camelcase.go
@@ -55,7 +55,7 @@ func Split(src string) (entries []string) {
class := 0
// split into fields based on class of unicode character
for _, r := range src {
- switch true {
+ switch {
case unicode.IsLower(r):
class = 1
case unicode.IsUpper(r):
@@ -86,5 +86,6 @@ func Split(src string) (entries []string) {
entries = append(entries, string(s))
}
}
- return
+
+ return entries
}
diff --git a/rootless.md b/rootless.md
index d8997a261..93a2b140f 100644
--- a/rootless.md
+++ b/rootless.md
@@ -44,3 +44,4 @@ can easily fail
* If a build is attempting to use a UID that is not mapped into the user namespace mapping for a container, then builds will not be able to put the UID in an image.
* Making device nodes within a container fails, even when running --privileged.
* The kernel does not allow non root user processes (processes without CAP_MKNOD) to create device nodes. If a container needs to create device nodes, it must be run as root.
+* When using --net=host with rootless containers, subsequent podman execs to that container will not join the host network namespace because it is owned by root.
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"}))
+ })
})
diff --git a/vendor/github.com/fatih/camelcase/.travis.yml b/vendor/github.com/fatih/camelcase/.travis.yml
deleted file mode 100644
index 3489e3871..000000000
--- a/vendor/github.com/fatih/camelcase/.travis.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-language: go
-go: 1.x
-
diff --git a/vendor/gopkg.in/yaml.v2/scannerc.go b/vendor/gopkg.in/yaml.v2/scannerc.go
index b33bdbaec..0b9bb6030 100644
--- a/vendor/gopkg.in/yaml.v2/scannerc.go
+++ b/vendor/gopkg.in/yaml.v2/scannerc.go
@@ -626,32 +626,18 @@ func trace(args ...interface{}) func() {
func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
// While we need more tokens to fetch, do it.
for {
- // Check if we really need to fetch more tokens.
- need_more_tokens := false
-
- if parser.tokens_head == len(parser.tokens) {
- // Queue is empty.
- need_more_tokens = true
- } else {
- // Check if any potential simple key may occupy the head position.
- for i := len(parser.simple_keys) - 1; i >= 0; i-- {
- simple_key := &parser.simple_keys[i]
- if simple_key.token_number < parser.tokens_parsed {
- break
- }
- if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok {
- return false
- } else if valid && simple_key.token_number == parser.tokens_parsed {
- need_more_tokens = true
- break
- }
+ if parser.tokens_head != len(parser.tokens) {
+ // If queue is non-empty, check if any potential simple key may
+ // occupy the head position.
+ head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
+ if !ok {
+ break
+ } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok {
+ return false
+ } else if !valid {
+ break
}
}
-
- // We are finished.
- if !need_more_tokens {
- break
- }
// Fetch the next token.
if !yaml_parser_fetch_next_token(parser) {
return false
@@ -883,6 +869,7 @@ func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {
return false
}
parser.simple_keys[len(parser.simple_keys)-1] = simple_key
+ parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1
}
return true
}
@@ -897,9 +884,10 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {
"while scanning a simple key", parser.simple_keys[i].mark,
"could not find expected ':'")
}
+ // Remove the key from the stack.
+ parser.simple_keys[i].possible = false
+ delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number)
}
- // Remove the key from the stack.
- parser.simple_keys[i].possible = false
return true
}
@@ -930,7 +918,9 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {
if parser.flow_level > 0 {
parser.flow_level--
- parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1]
+ last := len(parser.simple_keys) - 1
+ delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number)
+ parser.simple_keys = parser.simple_keys[:last]
}
return true
}
@@ -1007,6 +997,8 @@ func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {
// Initialize the simple key stack.
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
+ parser.simple_keys_by_tok = make(map[int]int)
+
// A simple key is allowed at the beginning of the stream.
parser.simple_key_allowed = true
@@ -1310,6 +1302,7 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
// Remove the simple key.
simple_key.possible = false
+ delete(parser.simple_keys_by_tok, simple_key.token_number)
// A simple key cannot follow another simple key.
parser.simple_key_allowed = false
diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go
index e25cee563..f6a9c8e34 100644
--- a/vendor/gopkg.in/yaml.v2/yamlh.go
+++ b/vendor/gopkg.in/yaml.v2/yamlh.go
@@ -579,6 +579,7 @@ type yaml_parser_t struct {
simple_key_allowed bool // May a simple key occur at the current position?
simple_keys []yaml_simple_key_t // The stack of simple keys.
+ simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number
// Parser stuff
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2aa86270a..df323e0ff 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -261,8 +261,6 @@ github.com/docker/spdystream
github.com/docker/spdystream/spdy
# github.com/etcd-io/bbolt v1.3.3
github.com/etcd-io/bbolt
-# github.com/fatih/camelcase v1.0.0
-github.com/fatih/camelcase
# github.com/fsnotify/fsnotify v1.4.7
github.com/fsnotify/fsnotify
# github.com/fsouza/go-dockerclient v1.6.0
@@ -446,7 +444,7 @@ github.com/prometheus/common/model
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
-# github.com/rootless-containers/rootlesskit v0.7.1
+# github.com/rootless-containers/rootlesskit v0.7.2
github.com/rootless-containers/rootlesskit/pkg/msgutil
github.com/rootless-containers/rootlesskit/pkg/port
github.com/rootless-containers/rootlesskit/pkg/port/builtin
@@ -627,7 +625,7 @@ gopkg.in/square/go-jose.v2/cipher
gopkg.in/square/go-jose.v2/json
# gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
gopkg.in/tomb.v1
-# gopkg.in/yaml.v2 v2.2.7
+# gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2
# k8s.io/api v0.17.2
k8s.io/api/core/v1