aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/port.go49
-rw-r--r--cmd/podman/shared/create.go5
-rw-r--r--cmd/podman/shared/intermediate.go1
-rw-r--r--completions/bash/podman6
-rw-r--r--docs/source/markdown/podman-build.1.md17
-rw-r--r--docs/source/markdown/podman-create.1.md21
-rw-r--r--docs/source/markdown/podman-run.1.md7
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--pkg/adapter/containers.go6
-rw-r--r--pkg/api/handlers/images.go33
-rw-r--r--pkg/api/handlers/libpod/images.go39
-rw-r--r--pkg/bindings/connection.go9
-rw-r--r--pkg/bindings/containers/containers.go45
-rw-r--r--pkg/bindings/images/images.go82
-rw-r--r--pkg/bindings/images/search.go9
-rw-r--r--pkg/bindings/pods/pods.go17
-rw-r--r--pkg/bindings/test/common_test.go13
-rw-r--r--pkg/bindings/test/images_test.go165
-rw-r--r--pkg/bindings/volumes/volumes.go5
-rw-r--r--test/e2e/healthcheck_run_test.go20
-rw-r--r--test/system/500-networking.bats63
-rw-r--r--vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go123
-rw-r--r--vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go70
-rw-r--r--vendor/modules.txt2
26 files changed, 583 insertions, 232 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 6fa2b3c71..4eeb09d42 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -396,6 +396,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"Assign a name to the container",
)
createFlags.Bool(
+ "no-healthcheck", false,
+ "Disable healthchecks on container",
+ )
+ createFlags.Bool(
"oom-kill-disable", false,
"Disable OOM Killer",
)
diff --git a/cmd/podman/port.go b/cmd/podman/port.go
index eef3d4b1d..4bb79a3a2 100644
--- a/cmd/podman/port.go
+++ b/cmd/podman/port.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/pkg/adapter"
+ "github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var (
portDescription = `List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT
`
_portCommand = &cobra.Command{
- Use: "port [flags] CONTAINER",
+ Use: "port [flags] CONTAINER [PORT]",
Short: "List port mappings or a specific mapping for the container",
Long: portDescription,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -48,8 +49,8 @@ func init() {
func portCmd(c *cliconfig.PortValues) error {
var (
- userProto string
- userPort int
+ userPort nat.Port
+ err error
)
args := c.InputArgs
@@ -70,25 +71,19 @@ func portCmd(c *cliconfig.PortValues) error {
if len(args) == 1 && c.Latest {
port = args[0]
}
- if port != "" {
+ if len(port) > 0 {
fields := strings.Split(port, "/")
- // User supplied at least port
- var err error
- // User supplied port and protocol
- if len(fields) == 2 {
- userProto = fields[1]
- }
- if len(fields) >= 1 {
- p := fields[0]
- userPort, err = strconv.Atoi(p)
- if err != nil {
- return errors.Wrapf(err, "unable to format port")
- }
- }
- // Format is incorrect
if len(fields) > 2 || len(fields) < 1 {
return errors.Errorf("port formats are port/protocol. '%s' is invalid", port)
}
+ if len(fields) == 1 {
+ fields = append(fields, "tcp")
+ }
+
+ userPort, err = nat.NewPort(fields[1], fields[0])
+ if err != nil {
+ return err
+ }
}
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
@@ -96,7 +91,6 @@ func portCmd(c *cliconfig.PortValues) error {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.DeferredShutdown(false)
-
containers, err := runtime.Port(c)
if err != nil {
return err
@@ -122,17 +116,18 @@ func portCmd(c *cliconfig.PortValues) error {
fmt.Printf("%d/%s -> %s:%d\n", v.ContainerPort, v.Protocol, hostIP, v.HostPort)
continue
}
- // We have a match on ports
- if v.ContainerPort == int32(userPort) {
- if userProto == "" || userProto == v.Protocol {
- fmt.Printf("%s:%d\n", hostIP, v.HostPort)
- found = true
- break
- }
+ containerPort, err := nat.NewPort(v.Protocol, strconv.Itoa(int(v.ContainerPort)))
+ if err != nil {
+ return err
+ }
+ if containerPort == userPort {
+ fmt.Printf("%s:%d\n", hostIP, v.HostPort)
+ found = true
+ break
}
}
if !found && port != "" {
- return errors.Errorf("failed to find published port '%d'", userPort)
+ return errors.Errorf("failed to find published port %q", port)
}
}
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 5b244699c..0814eeba3 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -120,12 +120,13 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.
imageName = newImage.ID()
}
- // if the user disabled the healthcheck with "none", we skip adding it
+ // if the user disabled the healthcheck with "none" or the no-healthcheck
+ // options is provided, we skip adding it
healthCheckCommandInput := c.String("healthcheck-command")
// the user didn't disable the healthcheck but did pass in a healthcheck command
// now we need to make a healthcheck from the commandline input
- if healthCheckCommandInput != "none" {
+ if healthCheckCommandInput != "none" && !c.Bool("no-healthcheck") {
if len(healthCheckCommandInput) > 0 {
healthCheck, err = makeHealthCheckFromCli(c)
if err != nil {
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index ee212234f..e76750042 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -425,6 +425,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["memory-swappiness"] = newCRInt64(c, "memory-swappiness")
m["name"] = newCRString(c, "name")
m["network"] = newCRString(c, "network")
+ m["no-healthcheck"] = newCRBool(c, "no-healthcheck")
m["no-hosts"] = newCRBool(c, "no-hosts")
m["oom-kill-disable"] = newCRBool(c, "oom-kill-disable")
m["oom-score-adj"] = newCRInt(c, "oom-score-adj")
diff --git a/completions/bash/podman b/completions/bash/podman
index 958633bf0..13be64e06 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1888,6 +1888,11 @@ _podman_container_run() {
--expose
--gidmap
--group-add
+ --health-cmd
+ --health-interval
+ --health-retries
+ --health-start-period
+ --health-timeout
--hostname -h
--http-proxy
--image-volume
@@ -1906,6 +1911,7 @@ _podman_container_run() {
--memory-reservation
--name
--network
+ --no-healthcheck
--no-hosts
--oom-score-adj
--pid
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index 738644c16..12f099e65 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -172,13 +172,20 @@ 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.
-**--device**=*device*
+**--device**=_host-device_[**:**_container-device_][**:**_permissions_]
-Add a host device to the container. The format is `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
+Add a host device to the container. Optional *permissions* parameter
+can be used to specify device permissions, it is combination of
+**r** for read, **w** for write, and **m** for **mknod**(2).
-Note: if the user only has access rights via a group then accessing the device
-from inside a rootless container will fail. The `crun` runtime offers a
-workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`.
+Example: **--device=/dev/sdc:/dev/xvdc:rwm**.
+
+Note: if _host_device_ is a symbolic link then it will be resolved first.
+The container will only store the major and minor numbers of the host device.
+
+Note: if the user only has access rights via a group, accessing the device
+from inside a rootless container will fail. The **crun**(1) runtime offers a
+workaround for this by adding the option **--annotation run.oci.keep_original_groups=1**.
**--disable-compression, -D**
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index ca38be6a1..3c5f81764 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -201,13 +201,20 @@ it in the **libpod.conf** file: see **libpod.conf(5)** for more information.
Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-<value>` characters where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature. The default is *ctrl-p,ctrl-q*.
-**--device**=*device*
+**--device**=_host-device_[**:**_container-device_][**:**_permissions_]
-Add a host device to the container. The format is `<device-on-host>[:<device-on-container>][:<permissions>]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
+Add a host device to the container. Optional *permissions* parameter
+can be used to specify device permissions, it is combination of
+**r** for read, **w** for write, and **m** for **mknod**(2).
-Note: if the user only has access rights via a group then accessing the device
-from inside a rootless container will fail. The `crun` runtime offers a
-workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`.
+Example: **--device=/dev/sdc:/dev/xvdc:rwm**.
+
+Note: if _host_device_ is a symbolic link then it will be resolved first.
+The container will only store the major and minor numbers of the host device.
+
+Note: if the user only has access rights via a group, accessing the device
+from inside a rootless container will fail. The **crun**(1) runtime offers a
+workaround for this by adding the option **--annotation run.oci.keep_original_groups=1**.
**--device-cgroup-rule**="type major:minor mode"
@@ -557,6 +564,10 @@ Valid values are:
Not implemented
+**--no-healthcheck**=*true|false*
+
+Disable any defined healthchecks for container.
+
**--no-hosts**=*true|false*
Do not create /etc/hosts for the container.
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index f391307b3..220b32a46 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -218,6 +218,9 @@ can be used to specify device permissions, it is combination of
Example: **--device=/dev/sdc:/dev/xvdc:rwm**.
+Note: if _host_device_ is a symbolic link then it will be resolved first.
+The container will only store the major and minor numbers of the host device.
+
Note: if the user only has access rights via a group, accessing the device
from inside a rootless container will fail. The **crun**(1) runtime offers a
workaround for this by adding the option **--annotation run.oci.keep_original_groups=1**.
@@ -560,6 +563,10 @@ Valid _mode_ values are:
Not implemented.
+**--no-healthcheck**=*true|false*
+
+Disable any defined healthchecks for container.
+
**--no-hosts**=**true**|**false**
Do not create _/etc/hosts_ for the container.
diff --git a/go.mod b/go.mod
index fac401208..d8b4f0a6f 100644
--- a/go.mod
+++ b/go.mod
@@ -42,7 +42,7 @@ 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.1
+ github.com/opencontainers/selinux v1.3.2
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
diff --git a/go.sum b/go.sum
index a1cf437a8..f193da728 100644
--- a/go.sum
+++ b/go.sum
@@ -383,6 +383,8 @@ github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOl
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/opencontainers/selinux v1.3.2 h1:DR4lL9SYVjgcTZKEZIncvDU06fKSc/eygjmNGOA3E1s=
+github.com/opencontainers/selinux v1.3.2/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/imagebuilder v1.1.1 h1:KAUR31p8UBJdfVO42azWgb+LeMAed2zaKQ19e0C0X2I=
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index ab4255f89..78057e3f9 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -1116,7 +1116,11 @@ func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
)
if !c.All {
- containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
+ names := []string{}
+ if len(c.InputArgs) >= 1 {
+ names = []string{c.InputArgs[0]}
+ }
+ containers, err = shortcuts.GetContainersByContext(false, c.Latest, names, r.Runtime)
} else {
containers, err = r.Runtime.GetRunningContainers()
}
diff --git a/pkg/api/handlers/images.go b/pkg/api/handlers/images.go
index 96bcbdc96..2086ce748 100644
--- a/pkg/api/handlers/images.go
+++ b/pkg/api/handlers/images.go
@@ -7,6 +7,7 @@ import (
"os"
"strconv"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers/utils"
@@ -147,10 +148,36 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- // TODO filters are a bit undefined here in terms of what exactly the input looks
- // like. We need to understand that a bit more.
+
+ filter := image.SearchFilter{}
+ if len(query.Filters) > 0 {
+ if len(query.Filters["stars"]) > 0 {
+ stars, err := strconv.Atoi(query.Filters["stars"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ filter.Stars = stars
+ }
+ if len(query.Filters["is-official"]) > 0 {
+ isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ filter.IsOfficial = types.NewOptionalBool(isOfficial)
+ }
+ if len(query.Filters["is-automated"]) > 0 {
+ isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ filter.IsAutomated = types.NewOptionalBool(isAutomated)
+ }
+ }
options := image.SearchOptions{
- Filter: image.SearchFilter{},
+ Filter: filter,
Limit: query.Limit,
}
results, err := image.SearchImages(query.Term, options)
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index eac0e4dad..71603e6cc 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -8,6 +8,7 @@ import (
"net/http"
"os"
"strconv"
+ "strings"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
@@ -133,11 +134,16 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
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
+ dangling := query.Filters["all"]
+ if len(dangling) > 0 {
+ all, err = strconv.ParseBool(query.Filters["all"][0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
}
+ // dangling is special and not implemented in the libpod side of things
+ delete(query.Filters, "dangling")
for k, v := range query.Filters {
libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
}
@@ -157,7 +163,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
Compress bool `schema:"compress"`
Format string `schema:"format"`
}{
- // override any golang type defaults
+ Format: "docker-archive",
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
@@ -166,11 +172,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
return
}
- if len(query.Format) < 1 {
- utils.InternalServerError(w, errors.New("format parameter cannot be empty."))
- return
- }
-
tmpfile, err := ioutil.TempFile("", "api.tar")
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
@@ -186,6 +187,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.ImageNotFound(w, name, err)
return
}
+
if err := newImage.Save(r.Context(), name, query.Format, tmpfile.Name(), []string{}, false, query.Compress); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
return
@@ -234,8 +236,20 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image"))
return
}
-
- utils.WriteResponse(w, http.StatusOK, []handlers.LibpodImagesLoadReport{{ID: loadedImage}})
+ split := strings.Split(loadedImage, ",")
+ newImage, err := runtime.ImageRuntime().NewFromLocal(split[0])
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ // TODO this should go into libpod proper at some point.
+ if len(query.Reference) > 0 {
+ if err := newImage.TagImage(query.Reference); err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ }
+ utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage})
}
func ImagesImport(w http.ResponseWriter, r *http.Request) {
@@ -275,7 +289,6 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) {
tmpfile.Close()
source = tmpfile.Name()
}
-
importedImage, err := runtime.Import(context.Background(), source, query.Reference, query.Changes, query.Message, true)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import image"))
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 75f1fc6a5..ba5f9c3aa 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -206,7 +206,7 @@ func unixClient(_url *url.URL) (*http.Client, error) {
}
// 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) {
+func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string, queryParams url.Values, pathValues ...string) (*APIResponse, error) {
var (
err error
response *http.Response
@@ -225,12 +225,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
return nil, err
}
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 queryParams {
- r.Add(k, v)
- }
- req.URL.RawQuery = r.Encode()
+ req.URL.RawQuery = queryParams.Encode()
}
// Give the Do three chances in the case of a comm/service hiccup
for i := 0; i < 3; i++ {
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index a437e9a9b..2985787a6 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -3,6 +3,7 @@ package containers
import (
"context"
"net/http"
+ "net/url"
"strconv"
"github.com/containers/libpod/libpod"
@@ -21,28 +22,28 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int
return nil, err
}
var containers []lpapiv2.ListContainer
- params := make(map[string]string)
+ params := url.Values{}
if all != nil {
- params["all"] = strconv.FormatBool(*all)
+ params.Set("all", strconv.FormatBool(*all))
}
if last != nil {
- params["last"] = strconv.Itoa(*last)
+ params.Set("last", strconv.Itoa(*last))
}
if pod != nil {
- params["pod"] = strconv.FormatBool(*pod)
+ params.Set("pod", strconv.FormatBool(*pod))
}
if size != nil {
- params["size"] = strconv.FormatBool(*size)
+ params.Set("size", strconv.FormatBool(*size))
}
if sync != nil {
- params["sync"] = strconv.FormatBool(*sync)
+ params.Set("sync", strconv.FormatBool(*sync))
}
if filters != nil {
filterString, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = filterString
+ params.Set("filters", filterString)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params)
if err != nil {
@@ -63,13 +64,13 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if filters != nil {
filterString, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = filterString
+ params.Set("filters", filterString)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params)
if err != nil {
@@ -86,12 +87,12 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if force != nil {
- params["force"] = strconv.FormatBool(*force)
+ params.Set("force", strconv.FormatBool(*force))
}
if volumes != nil {
- params["vols"] = strconv.FormatBool(*volumes)
+ params.Set("vols", strconv.FormatBool(*volumes))
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nameOrID)
if err != nil {
@@ -109,9 +110,9 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*libpod.InspectC
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if size != nil {
- params["size"] = strconv.FormatBool(*size)
+ params.Set("size", strconv.FormatBool(*size))
}
response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nameOrID)
if err != nil {
@@ -129,8 +130,8 @@ func Kill(ctx context.Context, nameOrID string, signal string) error {
if err != nil {
return err
}
- params := make(map[string]string)
- params["signal"] = signal
+ params := url.Values{}
+ params.Set("signal", signal)
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nameOrID)
if err != nil {
return err
@@ -162,9 +163,9 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if timeout != nil {
- params["t"] = strconv.Itoa(*timeout)
+ params.Set("t", strconv.Itoa(*timeout))
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nameOrID)
if err != nil {
@@ -181,9 +182,9 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if detachKeys != nil {
- params["detachKeys"] = *detachKeys
+ params.Set("detachKeys", *detachKeys)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nameOrID)
if err != nil {
@@ -242,13 +243,13 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) {
// 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)
+ params := url.Values{}
conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
if timeout != nil {
- params["t"] = strconv.Itoa(*timeout)
+ params.Set("t", strconv.Itoa(*timeout))
}
response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nameOrID)
if err != nil {
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 271d58952..c84aa4601 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -2,8 +2,10 @@ package images
import (
"context"
+ "errors"
"io"
"net/http"
+ "net/url"
"strconv"
"github.com/containers/libpod/pkg/api/handlers"
@@ -33,16 +35,16 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handl
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if all != nil {
- params["all"] = strconv.FormatBool(*all)
+ params.Set("all", strconv.FormatBool(*all))
}
if filters != nil {
strFilters, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = strFilters
+ params.Set("filters", strFilters)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params)
if err != nil {
@@ -58,9 +60,9 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageD
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if size != nil {
- params["size"] = strconv.FormatBool(*size)
+ params.Set("size", strconv.FormatBool(*size))
}
inspectedData := inspect.ImageData{}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID)
@@ -88,15 +90,21 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse,
return history, response.Process(&history)
}
-func Load(ctx context.Context, r io.Reader) error {
+func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
+ var id handlers.IDResponse
conn, err := bindings.GetClient(ctx)
if err != nil {
- return err
+ return "", err
}
- // TODO this still needs error handling added
- //_, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint
- _ = conn
- return bindings.ErrNotImplemented
+ params := url.Values{}
+ if name != nil {
+ params.Set("reference", *name)
+ }
+ response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params)
+ if err != nil {
+ return "", err
+ }
+ return id.ID, response.Process(&id)
}
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
@@ -107,9 +115,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]str
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if force != nil {
- params["force"] = strconv.FormatBool(*force)
+ params.Set("force", strconv.FormatBool(*force))
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID)
if err != nil {
@@ -125,12 +133,12 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if format != nil {
- params["format"] = *format
+ params.Set("format", *format)
}
if compress != nil {
- params["compress"] = strconv.FormatBool(*compress)
+ params.Set("compress", strconv.FormatBool(*compress))
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nameOrID)
if err != nil {
@@ -153,13 +161,13 @@ func Prune(ctx context.Context, filters map[string][]string) ([]string, error) {
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if filters != nil {
stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = stringFilter
+ params.Set("filters", stringFilter)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params)
if err != nil {
@@ -174,9 +182,9 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error {
if err != nil {
return err
}
- params := make(map[string]string)
- params["tag"] = tag
- params["repo"] = repo
+ params := url.Values{}
+ params.Set("tag", tag)
+ params.Set("repo", repo)
response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/tag", params, nameOrID)
if err != nil {
return err
@@ -185,3 +193,35 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error {
}
func Build(nameOrId string) {}
+
+// Imports adds the given image to the local image store. This can be done by file and the given reader
+// or via the url parameter. Additional metadata can be associated with the image by using the changes and
+// message parameters. The image can also be tagged given a reference. One of url OR r must be provided.
+func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (string, error) {
+ var id handlers.IDResponse
+ if r != nil && u != nil {
+ return "", errors.New("url and r parameters cannot be used together")
+ }
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return "", err
+ }
+ params := url.Values{}
+ for _, change := range changes {
+ params.Add("changes", change)
+ }
+ if message != nil {
+ params.Set("message", *message)
+ }
+ if reference != nil {
+ params.Set("reference", *reference)
+ }
+ if u != nil {
+ params.Set("url", *u)
+ }
+ response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params)
+ if err != nil {
+ return "", err
+ }
+ return id.ID, response.Process(&id)
+}
diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go
index dca1b0e63..183ff3d77 100644
--- a/pkg/bindings/images/search.go
+++ b/pkg/bindings/images/search.go
@@ -3,6 +3,7 @@ package images
import (
"context"
"net/http"
+ "net/url"
"strconv"
"github.com/containers/libpod/libpod/image"
@@ -20,17 +21,17 @@ func Search(ctx context.Context, term string, limit *int, filters map[string][]s
if err != nil {
return nil, err
}
- params := make(map[string]string)
- params["term"] = term
+ params := url.Values{}
+ params.Set("term", term)
if limit != nil {
- params["limit"] = strconv.Itoa(*limit)
+ params.Set("limit", strconv.Itoa(*limit))
}
if filters != nil {
stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = stringFilter
+ params.Set("filters", stringFilter)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params)
if err != nil {
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index 838b22e43..1a8c31be1 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -3,6 +3,7 @@ package pods
import (
"context"
"net/http"
+ "net/url"
"strconv"
"github.com/containers/libpod/libpod"
@@ -48,9 +49,9 @@ func Kill(ctx context.Context, nameOrID string, signal *string) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if signal != nil {
- params["signal"] = *signal
+ params.Set("signal", *signal)
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nameOrID)
if err != nil {
@@ -95,13 +96,13 @@ func List(ctx context.Context, filters map[string][]string) ([]*libpod.PodInspec
if err != nil {
return nil, err
}
- params := make(map[string]string)
+ params := url.Values{}
if filters != nil {
stringFilter, err := bindings.FiltersToString(filters)
if err != nil {
return nil, err
}
- params["filters"] = stringFilter
+ params.Set("filters", stringFilter)
}
response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params)
if err != nil {
@@ -130,9 +131,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if force != nil {
- params["force"] = strconv.FormatBool(*force)
+ params.Set("force", strconv.FormatBool(*force))
}
response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nameOrID)
if err != nil {
@@ -166,9 +167,9 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if timeout != nil {
- params["t"] = strconv.Itoa(*timeout)
+ params.Set("t", strconv.Itoa(*timeout))
}
response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nameOrID)
if err != nil {
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index 98d64bbaa..38f5014ca 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -20,9 +20,18 @@ type testImage struct {
}
const (
+ devPodmanBinaryLocation string = "../../../bin/podman"
defaultPodmanBinaryLocation string = "/usr/bin/podman"
)
+func getPodmanBinary() string {
+ _, err := os.Stat(devPodmanBinaryLocation)
+ if os.IsNotExist(err) {
+ return defaultPodmanBinaryLocation
+ }
+ return devPodmanBinaryLocation
+}
+
var (
ImageCacheDir = "/tmp/podman/imagecachedir"
LockTmpDir string
@@ -50,7 +59,7 @@ type bindingTest struct {
func (b *bindingTest) runPodman(command []string) *gexec.Session {
var cmd []string
- podmanBinary := defaultPodmanBinaryLocation
+ podmanBinary := getPodmanBinary()
val, ok := os.LookupEnv("PODMAN_BINARY")
if ok {
podmanBinary = val
@@ -166,7 +175,7 @@ func (b *bindingTest) restoreImageFromCache(i testImage) {
// and add or append the alpine image to it
func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, podName *string) {
cmd := []string{"run", "-dt"}
- if *insidePod && podName != nil {
+ if insidePod != nil && podName != nil {
pName := *podName
cmd = append(cmd, "--pod", pName)
} else if containerName != nil {
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 0b51c8c9e..8eef28502 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -3,6 +3,8 @@ package test_bindings
import (
"context"
"net/http"
+ "os"
+ "path/filepath"
"time"
"github.com/containers/libpod/pkg/bindings"
@@ -71,6 +73,14 @@ var _ = Describe("Podman images", func() {
// Inspect by long name
_, err = images.GetImage(connText, alpine.name, nil)
Expect(err).To(BeNil())
+ // TODO it looks like the images API alwaays returns size regardless
+ // of bool or not. What should we do ?
+ //Expect(data.Size).To(BeZero())
+
+ // Enabling the size parameter should result in size being populated
+ data, err = images.GetImage(connText, alpine.name, &trueFlag)
+ Expect(err).To(BeNil())
+ Expect(data.Size).To(BeNumerically(">", 0))
})
// Test to validate the remove image api
@@ -181,4 +191,159 @@ var _ = Describe("Podman images", func() {
Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
})
+ It("Image Exists", func() {
+ // exists on bogus image should be false, with no error
+ exists, err := images.Exists(connText, "foobar")
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeFalse())
+
+ // exists with shortname should be true
+ exists, err = images.Exists(connText, alpine.shortName)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+
+ // exists with fqname should be true
+ exists, err = images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+ })
+
+ It("Load|Import Image", func() {
+ // load an image
+ _, err := images.Remove(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ exists, err := images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeFalse())
+ f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
+ defer f.Close()
+ Expect(err).To(BeNil())
+ names, err := images.Load(connText, f, nil)
+ Expect(err).To(BeNil())
+ Expect(names).To(Equal(alpine.name))
+ exists, err = images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+
+ // load with a repo name
+ f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
+ Expect(err).To(BeNil())
+ _, err = images.Remove(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ exists, err = images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeFalse())
+ newName := "quay.io/newname:fizzle"
+ names, err = images.Load(connText, f, &newName)
+ Expect(err).To(BeNil())
+ Expect(names).To(Equal(alpine.name))
+ exists, err = images.Exists(connText, newName)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+
+ // load with a bad repo name should trigger a 500
+ f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
+ Expect(err).To(BeNil())
+ _, err = images.Remove(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ exists, err = images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeFalse())
+ badName := "quay.io/newName:fizzle"
+ _, err = images.Load(connText, f, &badName)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+ })
+
+ It("Export Image", func() {
+ // Export an image
+ exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName)
+ w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName))
+ defer w.Close()
+ Expect(err).To(BeNil())
+ err = images.Export(connText, alpine.name, w, nil, nil)
+ Expect(err).To(BeNil())
+ _, err = os.Stat(exportPath)
+ Expect(err).To(BeNil())
+
+ // TODO how do we verify that a format change worked?
+ })
+
+ It("Import Image", func() {
+ // load an image
+ _, err = images.Remove(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ exists, err := images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeFalse())
+ f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName))
+ defer f.Close()
+ Expect(err).To(BeNil())
+ changes := []string{"CMD /bin/foobar"}
+ testMessage := "test_import"
+ _, err = images.Import(connText, changes, &testMessage, &alpine.name, nil, f)
+ Expect(err).To(BeNil())
+ exists, err = images.Exists(connText, alpine.name)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+ data, err := images.GetImage(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ Expect(data.Comment).To(Equal(testMessage))
+
+ })
+ It("History Image", func() {
+ // a bogus name should return a 404
+ _, err := images.History(connText, "foobar")
+ Expect(err).To(Not(BeNil()))
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ var foundID bool
+ data, err := images.GetImage(connText, alpine.name, nil)
+ Expect(err).To(BeNil())
+ history, err := images.History(connText, alpine.name)
+ Expect(err).To(BeNil())
+ for _, i := range history {
+ if i.ID == data.ID {
+ foundID = true
+ break
+ }
+ }
+ Expect(foundID).To(BeTrue())
+ })
+
+ It("Search for an image", func() {
+ imgs, err := images.Search(connText, "alpine", nil, nil)
+ Expect(err).To(BeNil())
+ Expect(len(imgs)).To(BeNumerically(">", 1))
+ var foundAlpine bool
+ for _, i := range imgs {
+ if i.Name == "docker.io/library/alpine" {
+ foundAlpine = true
+ break
+ }
+ }
+ Expect(foundAlpine).To(BeTrue())
+
+ // Search for alpine with a limit of 10
+ ten := 10
+ imgs, err = images.Search(connText, "docker.io/alpine", &ten, nil)
+ Expect(err).To(BeNil())
+ Expect(len(imgs)).To(BeNumerically("<=", 10))
+
+ // Search for alpine with stars greater than 100
+ filters := make(map[string][]string)
+ filters["stars"] = []string{"100"}
+ imgs, err = images.Search(connText, "docker.io/alpine", nil, filters)
+ Expect(err).To(BeNil())
+ for _, i := range imgs {
+ Expect(i.Stars).To(BeNumerically(">=", 100))
+ }
+
+ // Search with a fqdn
+ imgs, err = images.Search(connText, "quay.io/libpod/alpine_nginx", nil, nil)
+ Expect(len(imgs)).To(BeNumerically(">=", 1))
+ })
+
})
diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go
index 8313a7460..7f6a9cc9b 100644
--- a/pkg/bindings/volumes/volumes.go
+++ b/pkg/bindings/volumes/volumes.go
@@ -3,6 +3,7 @@ package volumes
import (
"context"
"net/http"
+ "net/url"
"strconv"
"github.com/containers/libpod/libpod"
@@ -73,9 +74,9 @@ func Remove(ctx context.Context, nameOrID string, force *bool) error {
if err != nil {
return err
}
- params := make(map[string]string)
+ params := url.Values{}
if force != nil {
- params["force"] = strconv.FormatBool(*force)
+ params.Set("force", strconv.FormatBool(*force))
}
response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/%s/prune", params, nameOrID)
if err != nil {
diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go
index 7633261e3..19a8658ac 100644
--- a/test/e2e/healthcheck_run_test.go
+++ b/test/e2e/healthcheck_run_test.go
@@ -41,6 +41,26 @@ var _ = Describe("Podman healthcheck run", func() {
Expect(session).To(ExitWithError())
})
+ It("podman disable healthcheck with --no-healthcheck on valid container", func() {
+ SkipIfRemote()
+ session := podmanTest.Podman([]string{"run", "-dt", "--no-healthcheck", "--name", "hc", healthcheck})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
+ hc.WaitWithDefaultTimeout()
+ Expect(hc.ExitCode()).To(Equal(125))
+ })
+
+ It("podman disable healthcheck with --health-cmd=none on valid container", func() {
+ SkipIfRemote()
+ session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "none", "--name", "hc", healthcheck})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"})
+ hc.WaitWithDefaultTimeout()
+ Expect(hc.ExitCode()).To(Equal(125))
+ })
+
It("podman healthcheck on valid container", func() {
Skip("Extremely consistent flake - re-enable on debugging")
session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck})
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
new file mode 100644
index 000000000..cd836610b
--- /dev/null
+++ b/test/system/500-networking.bats
@@ -0,0 +1,63 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# Test podman local networking
+#
+
+load helpers
+
+# Copied from tsweeney's https://github.com/containers/libpod/issues/4827
+@test "podman networking: port on localhost" {
+ skip_if_remote
+ random_1=$(random_string 30)
+ random_2=$(random_string 30)
+
+ HOST_PORT=8080
+ SERVER=http://localhost:$HOST_PORT
+
+ # Create a test file with random content
+ INDEX1=$PODMAN_TMPDIR/hello.txt
+ echo $random_1 > $INDEX1
+
+ # Bind-mount this file with a different name to a container running httpd
+ run_podman run -d --name myweb -p "$HOST_PORT:80" \
+ -v $INDEX1:/var/www/index.txt \
+ -w /var/www \
+ busybox httpd -f -p 80
+ cid=$output
+
+ # In that container, create a second file, using exec and redirection
+ run_podman exec -i myweb sh -c "cat > index2.txt" <<<"$random_2"
+ # ...verify its contents as seen from container.
+ run_podman exec -i myweb cat /var/www/index2.txt
+ is "$output" "$random_2" "exec cat index2.txt"
+
+ # Verify http contents: curl from localhost
+ run curl -s $SERVER/index.txt
+ is "$output" "$random_1" "curl localhost:/index.txt"
+ run curl -s $SERVER/index2.txt
+ is "$output" "$random_2" "curl localhost:/index2.txt"
+
+ # Verify http contents: wget from a second container
+ run_podman run --rm --net=host busybox wget -qO - $SERVER/index.txt
+ is "$output" "$random_1" "podman wget /index.txt"
+ run_podman run --rm --net=host busybox wget -qO - $SERVER/index2.txt
+ is "$output" "$random_2" "podman wget /index2.txt"
+
+ # Tests #4889 - two-argument form of "podman ports" was broken
+ run_podman port myweb
+ is "$output" "80/tcp -> 0.0.0.0:$HOST_PORT" "port <cid>"
+ run_podman port myweb 80
+ is "$output" "0.0.0.0:$HOST_PORT" "port <cid> 80"
+ run_podman port myweb 80/tcp
+ is "$output" "0.0.0.0:$HOST_PORT" "port <cid> 80/tcp"
+
+ run_podman 125 port myweb 99/tcp
+ is "$output" 'Error: failed to find published port "99/tcp"'
+
+ # Clean up
+ run_podman stop -t 1 myweb
+ run_podman rm myweb
+ run_podman rmi busybox
+}
+
+# vim: filetype=sh
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 9fcfd0867..0e97a0778 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
@@ -11,6 +11,7 @@ import (
"io"
"io/ioutil"
"os"
+ "path"
"path/filepath"
"regexp"
"strconv"
@@ -37,15 +38,14 @@ const (
selinuxTag = "SELINUX"
xattrNameSelinux = "security.selinux"
stRdOnly = 0x01
- selinuxfsMagic = 0xf97cff8c
)
type selinuxState struct {
- enabledSet bool
- enabled bool
- selinuxfsSet bool
- selinuxfs string
- mcsList map[string]bool
+ enabledSet bool
+ enabled bool
+ selinuxfsOnce sync.Once
+ selinuxfs string
+ mcsList map[string]bool
sync.Mutex
}
@@ -62,6 +62,10 @@ var (
state = selinuxState{
mcsList: make(map[string]bool),
}
+
+ // for attrPath()
+ attrPathOnce sync.Once
+ haveThreadSelf bool
)
// Context is a representation of the SELinux label broken into 4 parts
@@ -98,14 +102,6 @@ func SetDisabled() {
state.setEnable(false)
}
-func (s *selinuxState) setSELinuxfs(selinuxfs string) string {
- s.Lock()
- defer s.Unlock()
- s.selinuxfsSet = true
- s.selinuxfs = selinuxfs
- return s.selinuxfs
-}
-
func verifySELinuxfsMount(mnt string) bool {
var buf syscall.Statfs_t
for {
@@ -118,7 +114,8 @@ func verifySELinuxfsMount(mnt string) bool {
}
return false
}
- if uint32(buf.Type) != uint32(selinuxfsMagic) {
+
+ if buf.Type != unix.SELINUX_MAGIC {
return false
}
if (buf.Flags & stRdOnly) != 0 {
@@ -166,33 +163,29 @@ func findSELinuxfs() string {
// if there is one, or an empty string in case of EOF or error.
func findSELinuxfsMount(s *bufio.Scanner) string {
for s.Scan() {
- txt := s.Text()
+ txt := s.Bytes()
// The first field after - is fs type.
// Safe as spaces in mountpoints are encoded as \040
- if !strings.Contains(txt, " - selinuxfs ") {
+ if !bytes.Contains(txt, []byte(" - selinuxfs ")) {
continue
}
const mPos = 5 // mount point is 5th field
- fields := strings.SplitN(txt, " ", mPos+1)
+ fields := bytes.SplitN(txt, []byte(" "), mPos+1)
if len(fields) < mPos+1 {
continue
}
- return fields[mPos-1]
+ return string(fields[mPos-1])
}
return ""
}
func (s *selinuxState) getSELinuxfs() string {
- s.Lock()
- selinuxfs := s.selinuxfs
- selinuxfsSet := s.selinuxfsSet
- s.Unlock()
- if selinuxfsSet {
- return selinuxfs
- }
+ s.selinuxfsOnce.Do(func() {
+ s.selinuxfs = findSELinuxfs()
+ })
- return s.setSELinuxfs(findSELinuxfs())
+ return s.selinuxfs
}
// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs
@@ -254,10 +247,17 @@ func getSELinuxPolicyRoot() string {
return filepath.Join(selinuxDir, readConfig(selinuxTypeTag))
}
-func isProcHandle(fh *os.File) (bool, error) {
+func isProcHandle(fh *os.File) error {
var buf unix.Statfs_t
err := unix.Fstatfs(int(fh.Fd()), &buf)
- return buf.Type == unix.PROC_SUPER_MAGIC, err
+ if err != nil {
+ return fmt.Errorf("statfs(%q) failed: %v", fh.Name(), err)
+ }
+ if buf.Type != unix.PROC_SUPER_MAGIC {
+ return fmt.Errorf("file %q is not on procfs", fh.Name())
+ }
+
+ return nil
}
func readCon(fpath string) (string, error) {
@@ -271,10 +271,8 @@ func readCon(fpath string) (string, error) {
}
defer in.Close()
- if ok, err := isProcHandle(in); err != nil {
+ if err := isProcHandle(in); err != nil {
return "", err
- } else if !ok {
- return "", fmt.Errorf("%s not on procfs", fpath)
}
var retval string
@@ -317,7 +315,7 @@ SetFSCreateLabel tells kernel the label to create all file system objects
created by this task. Setting label="" to return to default.
*/
func SetFSCreateLabel(label string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), label)
+ return writeAttr("fscreate", label)
}
/*
@@ -325,12 +323,12 @@ FSCreateLabel returns the default label the kernel which the kernel is using
for file system objects created by this task. "" indicates default.
*/
func FSCreateLabel() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()))
+ return readAttr("fscreate")
}
// CurrentLabel returns the SELinux label of the current process thread, or an error.
func CurrentLabel() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()))
+ return readAttr("current")
}
// PidLabel returns the SELinux label of the given pid, or an error.
@@ -343,10 +341,10 @@ ExecLabel returns the SELinux label that the kernel will use for any programs
that are executed by the current process thread, or an error.
*/
func ExecLabel() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()))
+ return readAttr("exec")
}
-func writeCon(fpath string, val string) error {
+func writeCon(fpath, val string) error {
if fpath == "" {
return ErrEmptyPath
}
@@ -362,10 +360,8 @@ func writeCon(fpath string, val string) error {
}
defer out.Close()
- if ok, err := isProcHandle(out); err != nil {
+ if err := isProcHandle(out); err != nil {
return err
- } else if !ok {
- return fmt.Errorf("%s not on procfs", fpath)
}
if val != "" {
@@ -379,6 +375,32 @@ func writeCon(fpath string, val string) error {
return nil
}
+func attrPath(attr string) string {
+ // Linux >= 3.17 provides this
+ const threadSelfPrefix = "/proc/thread-self/attr"
+
+ attrPathOnce.Do(func() {
+ st, err := os.Stat(threadSelfPrefix)
+ if err == nil && st.Mode().IsDir() {
+ haveThreadSelf = true
+ }
+ })
+
+ if haveThreadSelf {
+ return path.Join(threadSelfPrefix, attr)
+ }
+
+ return path.Join("/proc/self/task/", strconv.Itoa(syscall.Gettid()), "/attr/", attr)
+}
+
+func readAttr(attr string) (string, error) {
+ return readCon(attrPath(attr))
+}
+
+func writeAttr(attr, val string) error {
+ return writeCon(attrPath(attr), val)
+}
+
/*
CanonicalizeContext takes a context string and writes it to the kernel
the function then returns the context that the kernel will use. This function
@@ -415,7 +437,7 @@ SetExecLabel sets the SELinux label that the kernel will use for any programs
that are executed by the current process thread, or an error.
*/
func SetExecLabel(label string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label)
+ return writeAttr("exec", label)
}
/*
@@ -423,18 +445,18 @@ SetTaskLabel sets the SELinux label for the current thread, or an error.
This requires the dyntransition permission.
*/
func SetTaskLabel(label string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid()), label)
+ return writeAttr("current", label)
}
// SetSocketLabel takes a process label and tells the kernel to assign the
// label to the next socket that gets created
func SetSocketLabel(label string) error {
- return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid()), label)
+ return writeAttr("sockcreate", label)
}
// SocketLabel retrieves the current socket label setting
func SocketLabel() (string, error) {
- return readCon(fmt.Sprintf("/proc/self/task/%d/attr/sockcreate", syscall.Gettid()))
+ return readAttr("sockcreate")
}
// PeerLabel retrieves the label of the client on the other side of a socket
@@ -449,7 +471,7 @@ func SetKeyLabel(label string) error {
if os.IsNotExist(err) {
return nil
}
- if label == "" && os.IsPermission(err) && !GetEnabled() {
+ if label == "" && os.IsPermission(err) {
return nil
}
return err
@@ -505,19 +527,18 @@ func ReserveLabel(label string) {
}
func selinuxEnforcePath() string {
- return fmt.Sprintf("%s/enforce", getSelinuxMountPoint())
+ return path.Join(getSelinuxMountPoint(), "enforce")
}
// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
func EnforceMode() int {
var enforce int
- enforceS, err := readCon(selinuxEnforcePath())
+ enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
if err != nil {
return -1
}
-
- enforce, err = strconv.Atoi(string(enforceS))
+ enforce, err = strconv.Atoi(string(enforceB))
if err != nil {
return -1
}
@@ -529,7 +550,7 @@ SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
Disabled is not valid, since this needs to be set at boot time.
*/
func SetEnforceMode(mode int) error {
- return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode))
+ return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644)
}
/*
@@ -711,7 +732,7 @@ exit:
// SecurityCheckContext validates that the SELinux label is understood by the kernel
func SecurityCheckContext(val string) error {
- return writeCon(fmt.Sprintf("%s/context", getSelinuxMountPoint()), val)
+ return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644)
}
/*
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
index 67a9d8ee8..4e711a9f8 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go
@@ -3,76 +3,32 @@
package selinux
import (
- "syscall"
- "unsafe"
+ "golang.org/x/sys/unix"
)
-var _zero uintptr
-
// Returns a []byte slice if the xattr is set and nil otherwise
// Requires path and its attribute as arguments
func lgetxattr(path string, attr string) ([]byte, error) {
- var sz int
- pathBytes, err := syscall.BytePtrFromString(path)
- if err != nil {
- return nil, err
- }
- attrBytes, err := syscall.BytePtrFromString(attr)
- if err != nil {
- return nil, err
- }
-
// Start with a 128 length byte array
- sz = 128
- dest := make([]byte, sz)
- destBytes := unsafe.Pointer(&dest[0])
- _sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
-
- switch {
- case errno == syscall.ENODATA:
- return nil, errno
- case errno == syscall.ENOTSUP:
- return nil, errno
- case errno == syscall.ERANGE:
- // 128 byte array might just not be good enough,
- // A dummy buffer is used ``uintptr(0)`` to get real size
- // of the xattrs on disk
- _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0)
- sz = int(_sz)
- if sz < 0 {
+ dest := make([]byte, 128)
+ sz, errno := unix.Lgetxattr(path, attr, dest)
+ if errno == unix.ERANGE {
+ // Buffer too small, get the real size first
+ sz, errno = unix.Lgetxattr(path, attr, []byte{})
+ if errno != nil {
return nil, errno
}
+
dest = make([]byte, sz)
- destBytes := unsafe.Pointer(&dest[0])
- _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0)
- if errno != 0 {
- return nil, errno
- }
- case errno != 0:
+ sz, errno = unix.Lgetxattr(path, attr, dest)
+ }
+ if errno != nil {
return nil, errno
}
- sz = int(_sz)
+
return dest[:sz], nil
}
func lsetxattr(path string, attr string, data []byte, flags int) error {
- pathBytes, err := syscall.BytePtrFromString(path)
- if err != nil {
- return err
- }
- attrBytes, err := syscall.BytePtrFromString(attr)
- if err != nil {
- return err
- }
- var dataBytes unsafe.Pointer
- if len(data) > 0 {
- dataBytes = unsafe.Pointer(&data[0])
- } else {
- dataBytes = unsafe.Pointer(&_zero)
- }
- _, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0)
- if errno != 0 {
- return errno
- }
- return nil
+ return unix.Lsetxattr(path, attr, data, flags)
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 2d5e80c2e..e9ef9873f 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -405,7 +405,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.1
+# github.com/opencontainers/selinux v1.3.2
github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
# github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316