summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--cmd/podman/pull.go1
-rw-r--r--docs/source/markdown/podman-build.1.md6
-rw-r--r--docs/source/markdown/podman-pull.1.md16
-rw-r--r--docs/source/markdown/podman-remote.1.md2
-rw-r--r--docs/source/markdown/podman.1.md6
-rw-r--r--go.mod4
-rw-r--r--go.sum5
-rw-r--r--install.md10
-rw-r--r--pkg/api/handlers/generic/ping.go4
-rw-r--r--pkg/api/handlers/utils/errors.go11
-rw-r--r--pkg/api/handlers/version.go (renamed from pkg/api/handlers/generic/version.go)5
-rw-r--r--pkg/api/server/register_images.go23
-rw-r--r--pkg/api/server/register_version.go6
-rw-r--r--pkg/apparmor/apparmor_linux_template.go6
-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--vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go33
-rw-r--r--vendor/modules.txt4
41 files changed, 1362 insertions, 647 deletions
diff --git a/Makefile b/Makefile
index da58d2e8a..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)" \
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index c6baf6b61..1cbb3f45e 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -55,7 +55,6 @@ func init() {
flags.StringVar(&pullCommand.Creds, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.BoolVarP(&pullCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&pullCommand.OverrideArch, "override-arch", "", "use `ARCH` instead of the architecture of the machine for choosing images")
- markFlagHidden(flags, "override-arch")
flags.StringVar(&pullCommand.OverrideOS, "override-os", "", "use `OS` instead of the running OS for choosing images")
markFlagHidden(flags, "override-os")
// Disabled flags for the remote client
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index fac8296ad..0f3bfa0d3 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -633,11 +633,11 @@ $ podman build .
$ podman build -f Containerfile.simple .
-$ cat ~/Dockerfile | podman build -f - .
+$ cat $HOME/Dockerfile | podman build -f - .
$ podman build -f Dockerfile.simple -f Containerfile.notsosimple .
-$ podman build -f Dockerfile.in ~
+$ podman build -f Dockerfile.in $HOME
$ podman build -t imageName .
@@ -649,7 +649,7 @@ $ podman build --runtime-flag log-format=json .
$ podman build --runtime-flag debug .
-$ podman build --authfile /tmp/auths/myauths.json --cert-dir ~/auth --tls-verify=true --creds=username:password -t imageName -f Dockerfile.simple .
+$ podman build --authfile /tmp/auths/myauths.json --cert-dir $HOME/auth --tls-verify=true --creds=username:password -t imageName -f Dockerfile.simple .
$ podman build --memory 40m --cpu-period 10000 --cpu-quota 50000 --ulimit nofile=1024:1028 -t imageName .
diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md
index de9688f5e..a22d2db42 100644
--- a/docs/source/markdown/podman-pull.1.md
+++ b/docs/source/markdown/podman-pull.1.md
@@ -69,6 +69,10 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+**--override-arch**=ARCH
+
+Override the machine's default architecture of the image to be pulled. For example, `arm`.
+
**--quiet**, **-q**
Suppress output information when pulling images
@@ -134,6 +138,18 @@ Writing manifest to image destination
Storing signatures
03290064078cb797f3e0a530e78c20c13dd22a3dd3adf84a5da2127b48df0438
```
+
+```
+$ podman pull --override-arch=arm arm32v7/debian:stretch
+Trying to pull docker.io/arm32v7/debian:stretch...
+Getting image source signatures
+Copying blob b531ae4a3925 done
+Copying config 3cba58dad5 done
+Writing manifest to image destination
+Storing signatures
+3cba58dad5d9b35e755b48b634acb3fdd185ab1c996ac11510cc72c17780e13c
+```
+
## FILES
**registries.conf** (`/etc/containers/registries.conf`)
diff --git a/docs/source/markdown/podman-remote.1.md b/docs/source/markdown/podman-remote.1.md
index eab2405b2..a7297f3f2 100644
--- a/docs/source/markdown/podman-remote.1.md
+++ b/docs/source/markdown/podman-remote.1.md
@@ -135,7 +135,7 @@ the exit codes follow the `chroot` standard, see below:
## FILES
-**podman-remote.conf** (`~/.config/containers/podman-remote.conf`)
+**podman-remote.conf** (`$HOME/.config/containers/podman-remote.conf`)
The podman-remote.conf file is the default configuration file for the podman
remote client. It is in the TOML format. It is primarily used to keep track
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index acb546ddd..fc069a7b0 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -108,7 +108,7 @@ Storage driver option, Default storage driver options are configured in /etc/con
Output logging information to syslog as well as the console.
-On remote clients, logging is directed to the file ~/.config/containers/podman.log
+On remote clients, logging is directed to the file $HOME/.config/containers/podman.log
**--tmpdir**
@@ -266,9 +266,9 @@ Currently the slirp4netns package is required to be installed to create a networ
### **NOTE:** Unsupported file systems in rootless mode
-The Overlay file system (OverlayFS) is not supported in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package and to enable it by adding `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in the `~/.config/containers/storage.conf` file.
+The Overlay file system (OverlayFS) is not supported in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package and to enable it by adding `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options]` in the `$HOME/.config/containers/storage.conf` file.
-The Network File System (NFS) and other distributed file systems (for example: Lustre, Spectrum Scale, the General Parallel File System (GPFS)) are not supported when running in rootless mode as these file systems do not understand user namespace. However, rootless Podman can make use of an NFS Homedir by modifying the `~/.config/containers/storage.conf` to have the `graphroot` option point to a directory stored on local (Non NFS) storage.
+The Network File System (NFS) and other distributed file systems (for example: Lustre, Spectrum Scale, the General Parallel File System (GPFS)) are not supported when running in rootless mode as these file systems do not understand user namespace. However, rootless Podman can make use of an NFS Homedir by modifying the `$HOME/.config/containers/storage.conf` to have the `graphroot` option point to a directory stored on local (Non NFS) storage.
For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/libpod/blob/master/troubleshooting.md).
diff --git a/go.mod b/go.mod
index c4e6d64bf..403f710fe 100644
--- a/go.mod
+++ b/go.mod
@@ -49,12 +49,12 @@ require (
github.com/opencontainers/runc v1.0.0-rc9
github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7
github.com/opencontainers/runtime-tools v0.9.0
- github.com/opencontainers/selinux v1.3.0
+ github.com/opencontainers/selinux v1.3.1
github.com/opentracing/opentracing-go v1.1.0
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
diff --git a/go.sum b/go.sum
index cf0cf98ac..8746215d5 100644
--- a/go.sum
+++ b/go.sum
@@ -405,6 +405,8 @@ github.com/opencontainers/selinux v1.2.2 h1:Kx9J6eDG5/24A6DtUquGSpJQ+m2MUTahn4Ft
github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g=
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
+github.com/opencontainers/selinux v1.3.1 h1:dn2Rc3wTEvTB6iVqoFrKKeMb0uZ38ZheeyMu2h5C1TI=
+github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316 h1:enQG2QUGwug4fR1yM6hL0Fjzx6Km/exZY6RbSPwMu3o=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316/go.mod h1:dv+J0b/HWai0QnMVb37/H0v36klkLBi2TNpPeWDxX10=
github.com/openshift/api v3.9.1-0.20190810003144-27fb16909b15+incompatible h1:s55wx8JIG/CKnewev892HifTBrtKzMdvgB3rm4rxC2s=
@@ -456,6 +458,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=
@@ -629,6 +633,7 @@ golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE=
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/install.md b/install.md
index 05cbce7c9..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)
@@ -100,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
```
@@ -169,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
```
@@ -201,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
```
@@ -217,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/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/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/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/register_images.go b/pkg/api/server/register_images.go
index c59d3d379..f1cc0574c 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -195,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:
@@ -607,13 +607,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 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"
+ // - 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"
+ // - 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>`)
@@ -621,12 +621,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - `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
+ // type: string
// produces:
// - application/json
// responses:
@@ -753,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_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/apparmor/apparmor_linux_template.go b/pkg/apparmor/apparmor_linux_template.go
index 163ba3792..8d9a92ef7 100644
--- a/pkg/apparmor/apparmor_linux_template.go
+++ b/pkg/apparmor/apparmor_linux_template.go
@@ -17,6 +17,12 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) {
file,
umount,
+{{if ge .Version 208096}}
+ # Allow signals from privileged profiles and from within the same profile
+ signal (receive) peer=unconfined,
+ signal (send,receive) peer={{.Name}},
+{{end}}
+
deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir)
# deny write to files not in /proc/<number>/** or /proc/sys/**
deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
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/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
index 2d4e9f890..9fcfd0867 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
@@ -7,7 +7,6 @@ import (
"bytes"
"crypto/rand"
"encoding/binary"
- "errors"
"fmt"
"io"
"io/ioutil"
@@ -18,6 +17,8 @@ import (
"strings"
"sync"
"syscall"
+
+ "github.com/pkg/errors"
"golang.org/x/sys/unix"
)
@@ -253,6 +254,12 @@ func getSELinuxPolicyRoot() string {
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
}
+func isProcHandle(fh *os.File) (bool, error) {
+ var buf unix.Statfs_t
+ err := unix.Fstatfs(int(fh.Fd()), &buf)
+ return buf.Type == unix.PROC_SUPER_MAGIC, err
+}
+
func readCon(fpath string) (string, error) {
if fpath == "" {
return "", ErrEmptyPath
@@ -264,6 +271,12 @@ func readCon(fpath string) (string, error) {
}
defer in.Close()
+ if ok, err := isProcHandle(in); err != nil {
+ return "", err
+ } else if !ok {
+ return "", fmt.Errorf("%s not on procfs", fpath)
+ }
+
var retval string
if _, err := fmt.Fscanf(in, "%s", &retval); err != nil {
return "", err
@@ -276,7 +289,10 @@ func SetFileLabel(fpath string, label string) error {
if fpath == "" {
return ErrEmptyPath
}
- return lsetxattr(fpath, xattrNameSelinux, []byte(label), 0)
+ if err := lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil {
+ return errors.Wrapf(err, "failed to set file label on %s", fpath)
+ }
+ return nil
}
// FileLabel returns the SELinux label for this path or returns an error.
@@ -346,12 +362,21 @@ func writeCon(fpath string, val string) error {
}
defer out.Close()
+ if ok, err := isProcHandle(out); err != nil {
+ return err
+ } else if !ok {
+ return fmt.Errorf("%s not on procfs", fpath)
+ }
+
if val != "" {
_, err = out.Write([]byte(val))
} else {
_, err = out.Write(nil)
}
- return err
+ if err != nil {
+ return errors.Wrapf(err, "failed to set %s on procfs", fpath)
+ }
+ return nil
}
/*
@@ -394,7 +419,7 @@ func SetExecLabel(label string) error {
}
/*
-SetTaskLabel sets the SELinux label for the current thread, or an error.
+SetTaskLabel sets the SELinux label for the current thread, or an error.
This requires the dyntransition permission.
*/
func SetTaskLabel(label string) error {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6385ab250..4d96788a8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -403,7 +403,7 @@ github.com/opencontainers/runtime-tools/generate
github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/validate
-# github.com/opencontainers/selinux v1.3.0
+# github.com/opencontainers/selinux v1.3.1
github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
# github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316
@@ -444,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