summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--cmd/podman/containers/prune.go5
-rw-r--r--cmd/podman/containers/rm.go4
-rw-r--r--cmd/podman/networks/rm.go19
-rw-r--r--cmd/podman/pods/prune.go5
-rw-r--r--cmd/podman/pods/rm.go15
-rw-r--r--cmd/podman/volumes/prune.go14
-rw-r--r--cmd/podman/volumes/rm.go18
-rw-r--r--contrib/rootless-cni-infra/Containerfile5
-rw-r--r--contrib/rootless-cni-infra/README.md1
-rwxr-xr-xcontrib/rootless-cni-infra/rootless-cni-infra9
-rw-r--r--docs/source/markdown/podman-network-rm.1.md9
-rw-r--r--docs/source/markdown/podman-pod-rm.1.md9
-rw-r--r--docs/source/markdown/podman-rm.1.md2
-rw-r--r--docs/source/markdown/podman-rmi.1.md2
-rw-r--r--docs/source/markdown/podman-volume-rm.1.md9
-rw-r--r--docs/tutorials/rootless_tutorial.md2
-rw-r--r--libpod/container_internal_linux.go3
-rw-r--r--libpod/define/errors.go3
-rw-r--r--libpod/healthcheck_linux.go5
-rw-r--r--libpod/image/image.go72
-rw-r--r--libpod/rootless_cni_linux.go2
-rw-r--r--pkg/api/handlers/compat/containers_attach.go6
-rw-r--r--pkg/api/handlers/compat/containers_create.go13
-rw-r--r--pkg/api/handlers/compat/exec.go6
-rw-r--r--pkg/api/handlers/compat/images_build.go40
-rw-r--r--pkg/api/handlers/libpod/images.go47
-rw-r--r--pkg/api/handlers/libpod/manifests.go17
-rw-r--r--pkg/api/handlers/libpod/networks.go4
-rw-r--r--pkg/api/server/idle/tracker.go96
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/register_images.go9
-rw-r--r--pkg/api/server/server.go22
-rw-r--r--pkg/bindings/images/build.go5
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/volumes.go4
-rw-r--r--pkg/domain/infra/abi/manifest.go85
-rw-r--r--pkg/domain/infra/abi/network.go3
-rw-r--r--pkg/domain/infra/abi/volumes.go2
-rw-r--r--pkg/domain/infra/tunnel/images.go8
-rw-r--r--pkg/domain/infra/tunnel/volumes.go2
-rw-r--r--pkg/registries/registries.go5
-rw-r--r--pkg/spec/config_linux.go3
-rw-r--r--pkg/spec/spec.go19
-rw-r--r--pkg/specgen/generate/container.go43
-rw-r--r--pkg/specgen/generate/security.go3
-rw-r--r--test/apiv2/20-containers.at23
-rw-r--r--test/e2e/checkpoint_test.go2
-rw-r--r--test/e2e/common_test.go51
-rw-r--r--test/e2e/config.go4
-rw-r--r--test/e2e/containers_conf_test.go2
-rw-r--r--test/e2e/cp_test.go4
-rw-r--r--test/e2e/create_staticip_test.go4
-rw-r--r--test/e2e/create_test.go21
-rw-r--r--test/e2e/exec_test.go2
-rw-r--r--test/e2e/libpod_suite_remote_test.go12
-rw-r--r--test/e2e/libpod_suite_test.go14
-rw-r--r--test/e2e/libpod_suite_varlink_test.go9
-rw-r--r--test/e2e/login_logout_test.go8
-rw-r--r--test/e2e/manifest_test.go11
-rw-r--r--test/e2e/mount_test.go5
-rw-r--r--test/e2e/network_create_test.go4
-rw-r--r--test/e2e/network_test.go146
-rw-r--r--test/e2e/pause_test.go8
-rw-r--r--test/e2e/pod_pause_test.go2
-rw-r--r--test/e2e/pod_rm_test.go7
-rw-r--r--test/e2e/pod_stats_test.go4
-rw-r--r--test/e2e/ps_test.go2
-rw-r--r--test/e2e/pull_test.go100
-rw-r--r--test/e2e/push_test.go6
-rw-r--r--test/e2e/rm_test.go4
-rw-r--r--test/e2e/run_cgroup_parent_test.go4
-rw-r--r--test/e2e/run_cleanup_test.go2
-rw-r--r--test/e2e/run_cpu_test.go8
-rw-r--r--test/e2e/run_device_test.go2
-rw-r--r--test/e2e/run_memory_test.go38
-rw-r--r--test/e2e/run_networking_test.go18
-rw-r--r--test/e2e/run_privileged_test.go2
-rw-r--r--test/e2e/run_security_labels.go4
-rw-r--r--test/e2e/run_staticip_test.go2
-rw-r--r--test/e2e/run_test.go46
-rw-r--r--test/e2e/run_volume_test.go2
-rw-r--r--test/e2e/search_test.go14
-rw-r--r--test/e2e/stats_test.go4
-rw-r--r--test/e2e/systemd_test.go6
-rw-r--r--test/e2e/tree_test.go4
-rw-r--r--test/e2e/untag_test.go1
-rw-r--r--test/e2e/volume_ls_test.go2
-rw-r--r--test/e2e/volume_rm_test.go8
-rw-r--r--test/registries.conf1
-rw-r--r--test/system/020-tag.bats19
-rw-r--r--test/system/030-run.bats45
-rw-r--r--test/system/075-exec.bats4
-rw-r--r--test/system/500-networking.bats2
-rwxr-xr-xtest/system/build-testimage7
-rw-r--r--test/system/helpers.bash2
-rw-r--r--transfer.md8
97 files changed, 973 insertions, 470 deletions
diff --git a/Makefile b/Makefile
index 8a75c11fb..99a32eb13 100644
--- a/Makefile
+++ b/Makefile
@@ -643,7 +643,7 @@ install.libseccomp.sudo:
pkg/varlink/iopodman.go: .gopathok pkg/varlink/io.podman.varlink
ifneq (,$(findstring Linux,$(shell uname -s)))
# Only generate the varlink code on Linux (see issue #4814).
- GO111MODULE=off $(GO) generate ./pkg/varlink/...
+ $(GO) generate ./pkg/varlink/...
endif
API.md: pkg/varlink/io.podman.varlink
diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go
index 90dea2b45..cfe6765ac 100644
--- a/cmd/podman/containers/prune.go
+++ b/cmd/podman/containers/prune.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -25,6 +26,7 @@ var (
Long: pruneDescription,
RunE: prune,
Example: `podman container prune`,
+ Args: validate.NoArgs,
}
force bool
filter = []string{}
@@ -45,9 +47,6 @@ func prune(cmd *cobra.Command, args []string) error {
var (
pruneOptions = entities.ContainerPruneOptions{}
)
- if len(args) > 0 {
- return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
- }
if !force {
reader := bufio.NewReader(os.Stdin)
fmt.Println("WARNING! This will remove all non running containers.")
diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go
index 8d0c7920f..f8f12234d 100644
--- a/cmd/podman/containers/rm.go
+++ b/cmd/podman/containers/rm.go
@@ -105,7 +105,7 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit
}
responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions)
if err != nil {
- if setExit && len(namesOrIDs) < 2 {
+ if setExit {
setExitCode(err)
}
return err
@@ -132,7 +132,7 @@ func setExitCode(err error) {
switch {
case cause == define.ErrNoSuchCtr:
registry.SetExitCode(1)
- case strings.Contains(cause.Error(), define.ErrNoSuchImage.Error()):
+ case strings.Contains(cause.Error(), define.ErrNoSuchCtr.Error()):
registry.SetExitCode(1)
case cause == define.ErrCtrStateInvalid:
registry.SetExitCode(2)
diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go
index ac49993b7..86aad43cb 100644
--- a/cmd/podman/networks/rm.go
+++ b/cmd/podman/networks/rm.go
@@ -2,10 +2,13 @@ package network
import (
"fmt"
+ "strings"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -47,14 +50,30 @@ func networkRm(cmd *cobra.Command, args []string) error {
responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions)
if err != nil {
+ setExitCode(err)
return err
}
for _, r := range responses {
if r.Err == nil {
fmt.Println(r.Name)
} else {
+ setExitCode(r.Err)
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}
+
+func setExitCode(err error) {
+ cause := errors.Cause(err)
+ switch {
+ case cause == define.ErrNoSuchNetwork:
+ registry.SetExitCode(1)
+ case strings.Contains(cause.Error(), define.ErrNoSuchNetwork.Error()):
+ registry.SetExitCode(1)
+ case cause == define.ErrNetworkInUse:
+ registry.SetExitCode(2)
+ case strings.Contains(cause.Error(), define.ErrNetworkInUse.Error()):
+ registry.SetExitCode(2)
+ }
+}
diff --git a/cmd/podman/pods/prune.go b/cmd/podman/pods/prune.go
index a7347ede5..f13d95ae9 100644
--- a/cmd/podman/pods/prune.go
+++ b/cmd/podman/pods/prune.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -23,6 +24,7 @@ var (
pruneCommand = &cobra.Command{
Use: "prune [flags]",
+ Args: validate.NoArgs,
Short: "Remove all stopped pods and their containers",
Long: pruneDescription,
RunE: prune,
@@ -41,9 +43,6 @@ func init() {
}
func prune(cmd *cobra.Command, args []string) error {
- if len(args) > 0 {
- return errors.Errorf("`%s` takes no arguments", cmd.CommandPath())
- }
if !pruneOptions.Force {
reader := bufio.NewReader(os.Stdin)
fmt.Println("WARNING! This will remove all stopped/exited pods..")
diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go
index 0bb35e1ca..2975db3e8 100644
--- a/cmd/podman/pods/rm.go
+++ b/cmd/podman/pods/rm.go
@@ -3,12 +3,15 @@ package pods
import (
"context"
"fmt"
+ "strings"
"github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/cmd/podman/validate"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -73,6 +76,7 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b
responses, err := registry.ContainerEngine().PodRm(context.Background(), namesOrIDs, rmOptions)
if err != nil {
+ setExitCode(err)
return err
}
@@ -83,8 +87,19 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b
fmt.Println(r.Id)
}
} else {
+ setExitCode(r.Err)
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}
+
+func setExitCode(err error) {
+ cause := errors.Cause(err)
+ switch {
+ case cause == define.ErrNoSuchPod:
+ registry.SetExitCode(1)
+ case strings.Contains(cause.Error(), define.ErrNoSuchPod.Error()):
+ registry.SetExitCode(1)
+ }
+}
diff --git a/cmd/podman/volumes/prune.go b/cmd/podman/volumes/prune.go
index 95b47b726..78c258bec 100644
--- a/cmd/podman/volumes/prune.go
+++ b/cmd/podman/volumes/prune.go
@@ -29,10 +29,6 @@ var (
}
)
-var (
- pruneOptions entities.VolumePruneOptions
-)
-
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
@@ -40,12 +36,16 @@ func init() {
Parent: volumeCmd,
})
flags := pruneCommand.Flags()
- flags.BoolVarP(&pruneOptions.Force, "force", "f", false, "Do not prompt for confirmation")
+ flags.BoolP("force", "f", false, "Do not prompt for confirmation")
}
func prune(cmd *cobra.Command, args []string) error {
// Prompt for confirmation if --force is not set
- if !pruneOptions.Force {
+ force, err := cmd.Flags().GetBool("force")
+ if err != nil {
+ return err
+ }
+ if !force {
reader := bufio.NewReader(os.Stdin)
fmt.Println("WARNING! This will remove all volumes not used by at least one container.")
fmt.Print("Are you sure you want to continue? [y/N] ")
@@ -57,7 +57,7 @@ func prune(cmd *cobra.Command, args []string) error {
return nil
}
}
- responses, err := registry.ContainerEngine().VolumePrune(context.Background(), pruneOptions)
+ responses, err := registry.ContainerEngine().VolumePrune(context.Background())
if err != nil {
return err
}
diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go
index 5b23eb5e6..4c960d4d5 100644
--- a/cmd/podman/volumes/rm.go
+++ b/cmd/podman/volumes/rm.go
@@ -3,9 +3,11 @@ package volumes
import (
"context"
"fmt"
+ "strings"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -51,14 +53,30 @@ func rm(cmd *cobra.Command, args []string) error {
}
responses, err := registry.ContainerEngine().VolumeRm(context.Background(), args, rmOptions)
if err != nil {
+ setExitCode(err)
return err
}
for _, r := range responses {
if r.Err == nil {
fmt.Println(r.Id)
} else {
+ setExitCode(r.Err)
errs = append(errs, r.Err)
}
}
return errs.PrintErrors()
}
+
+func setExitCode(err error) {
+ cause := errors.Cause(err)
+ switch {
+ case cause == define.ErrNoSuchVolume:
+ registry.SetExitCode(1)
+ case strings.Contains(cause.Error(), define.ErrNoSuchVolume.Error()):
+ registry.SetExitCode(1)
+ case cause == define.ErrVolumeBeingUsed:
+ registry.SetExitCode(2)
+ case strings.Contains(cause.Error(), define.ErrVolumeBeingUsed.Error()):
+ registry.SetExitCode(2)
+ }
+}
diff --git a/contrib/rootless-cni-infra/Containerfile b/contrib/rootless-cni-infra/Containerfile
index 6bf70d644..dd80fda28 100644
--- a/contrib/rootless-cni-infra/Containerfile
+++ b/contrib/rootless-cni-infra/Containerfile
@@ -2,8 +2,7 @@ ARG GOLANG_VERSION=1.15
ARG ALPINE_VERSION=3.12
ARG CNI_VERSION=v0.8.0
ARG CNI_PLUGINS_VERSION=v0.8.7
-# Aug 20, 2020
-ARG DNSNAME_VESION=78b4da7bbfc51c27366da630e1df1c4f2e8b1b5b
+ARG DNSNAME_VESION=v1.0.0
FROM golang:${GOLANG_VERSION}-alpine${ALPINE_VERSION} AS golang-base
RUN apk add --no-cache git
@@ -34,4 +33,4 @@ COPY rootless-cni-infra /usr/local/bin
ENV CNI_PATH=/opt/cni/bin
CMD ["sleep", "infinity"]
-ENV ROOTLESS_CNI_INFRA_VERSION=2
+ENV ROOTLESS_CNI_INFRA_VERSION=3
diff --git a/contrib/rootless-cni-infra/README.md b/contrib/rootless-cni-infra/README.md
index 5aa13374b..c43b4cf49 100644
--- a/contrib/rootless-cni-infra/README.md
+++ b/contrib/rootless-cni-infra/README.md
@@ -22,3 +22,4 @@ The container images live on `quay.io/libpod/rootless-cni-infra`. The tags have
* `/run/rootless-cni-infra/${CONTAINER_ID}/pid`: PID of the `sleep infinity` process that corresponds to the allocated netns
* `/run/rootless-cni-infra/${CONTAINER_ID}/attached/${NETWORK_NAME}`: CNI result
+* `/run/rootless-cni-infra/${CONTAINER_ID}/attached-args/${NETWORK_NAME}`: CNI args
diff --git a/contrib/rootless-cni-infra/rootless-cni-infra b/contrib/rootless-cni-infra/rootless-cni-infra
index 5cb43621d..463254c7f 100755
--- a/contrib/rootless-cni-infra/rootless-cni-infra
+++ b/contrib/rootless-cni-infra/rootless-cni-infra
@@ -33,7 +33,7 @@ cmd_entrypoint_alloc() {
K8S_POD_NAME="$3"
dir="${BASE}/${ID}"
- mkdir -p "${dir}/attached"
+ mkdir -p "${dir}/attached" "${dir}/attached-args"
pid=""
if [ -f "${dir}/pid" ]; then
@@ -50,6 +50,7 @@ cmd_entrypoint_alloc() {
CNI_IFNAME="eth${nwcount}"
export CNI_ARGS CNI_IFNAME
cnitool add "${NET}" "/proc/${pid}/ns/net" >"${dir}/attached/${NET}"
+ echo "${CNI_ARGS}" >"${dir}/attached-args/${NET}"
# return the result
ns="/proc/${pid}/ns/net"
@@ -71,8 +72,12 @@ cmd_entrypoint_dealloc() {
exit 0
fi
pid=$(cat "${dir}/pid")
+ if [ -f "${dir}/attached-args/${NET}" ]; then
+ CNI_ARGS=$(cat "${dir}/attached-args/${NET}")
+ export CNI_ARGS
+ fi
cnitool del "${NET}" "/proc/${pid}/ns/net"
- rm -f "${dir}/attached/${NET}"
+ rm -f "${dir}/attached/${NET}" "${dir}/attached-args/${NET}"
nwcount=$(find "${dir}/attached" -type f | wc -l)
if [ "${nwcount}" = 0 ]; then
diff --git a/docs/source/markdown/podman-network-rm.1.md b/docs/source/markdown/podman-network-rm.1.md
index 9ce4d1cd8..616bb2514 100644
--- a/docs/source/markdown/podman-network-rm.1.md
+++ b/docs/source/markdown/podman-network-rm.1.md
@@ -31,6 +31,15 @@ Delete the `fred` network and all containers associated with the network.
Deleted: fred
```
+## Exit Status
+ **0** All specified networks removed
+
+ **1** One of the specified networks did not exist, and no other failures
+
+ **2** The network is in use by a container or a Pod
+
+ **125** The command fails for any other reason
+
## SEE ALSO
podman(1), podman-network(1), podman-network-inspect(1)
diff --git a/docs/source/markdown/podman-pod-rm.1.md b/docs/source/markdown/podman-pod-rm.1.md
index 95e7ab002..dd89694ec 100644
--- a/docs/source/markdown/podman-pod-rm.1.md
+++ b/docs/source/markdown/podman-pod-rm.1.md
@@ -49,6 +49,15 @@ podman pod rm -fa
podman pod rm --pod-id-file /path/to/id/file
+## Exit Status
+ **0** All specified pods removed
+
+ **1** One of the specified pods did not exist, and no other failures
+
+ **2** One of the specified pods is attached to a container
+
+ **125** The command fails for any other reason
+
## SEE ALSO
podman-pod(1)
diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md
index 990af0cd1..e3e6740df 100644
--- a/docs/source/markdown/podman-rm.1.md
+++ b/docs/source/markdown/podman-rm.1.md
@@ -93,7 +93,7 @@ $ podman rm -f --latest
**2** One of the specified containers is paused or running
- **125** The command fails for a reason other than container did not exist or is paused/running
+ **125** The command fails for any other reason
## SEE ALSO
podman(1), podman-image-rm(1), podman-ps(1), podman-build(1)
diff --git a/docs/source/markdown/podman-rmi.1.md b/docs/source/markdown/podman-rmi.1.md
index 58280e831..27fe3b235 100644
--- a/docs/source/markdown/podman-rmi.1.md
+++ b/docs/source/markdown/podman-rmi.1.md
@@ -47,7 +47,7 @@ $ podman rmi -a -f
**2** One of the specified images has child images or is being used by a container
- **125** The command fails for a reason other than an image did not exist or is in use
+ **125** The command fails for any other reason
## SEE ALSO
podman(1)
diff --git a/docs/source/markdown/podman-volume-rm.1.md b/docs/source/markdown/podman-volume-rm.1.md
index 9a2fe8c99..ed4a83f9e 100644
--- a/docs/source/markdown/podman-volume-rm.1.md
+++ b/docs/source/markdown/podman-volume-rm.1.md
@@ -39,6 +39,15 @@ $ podman volume rm --all
$ podman volume rm --force myvol
```
+## Exit Status
+ **0** All specified volumes removed
+
+ **1** One of the specified volumes did not exist, and no other failures
+
+ **2** One of the specified volumes is being used by a container
+
+ **125** The command fails for any other reason
+
## SEE ALSO
podman-volume(1)
diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md
index 6b83f18d9..3b9cbd2d0 100644
--- a/docs/tutorials/rootless_tutorial.md
+++ b/docs/tutorials/rootless_tutorial.md
@@ -95,7 +95,7 @@ If this is required, the administrator must verify that the UID of the user is p
To change its value the administrator can use a call similar to: `sysctl -w "net.ipv4.ping_group_range=0 2000000"`.
-To make the change persistent, the administrator will need to add a file in `/etc/sysctl.d` that contains `net.ipv4.ping_group_range=0 $MAX_UID`.
+To make the change persist, the administrator will need to add a file with the `.conf` file extension in `/etc/sysctl.d` that contains `net.ipv4.ping_group_range=0 $MAX_GID`, where `$MAX_GID` is the highest assignable GID of the user running the container.
## User Actions
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index dde7cafb1..eba732d2a 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -415,8 +415,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
// Look up and add groups the user belongs to, if a group wasn't directly specified
- if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") {
+ if !strings.Contains(c.config.User, ":") {
for _, gid := range execUser.Sgids {
+ // FIXME: We need to add a flag to containers.conf to not add these for HPC Users.
g.AddProcessAdditionalGid(uint32(gid))
}
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index b3f6483d1..627928ef7 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -162,6 +162,9 @@ var (
// in a pod. This cannot be done as the infra container has all the network information
ErrNetworkOnPodContainer = errors.New("network cannot be configured when it is shared with a pod")
+ // ErrNetworkInUse indicates the requested operation failed because the network was in use
+ ErrNetworkInUse = errors.New("network is being used")
+
// ErrStoreNotInitialized indicates that the container storage was never
// initialized.
ErrStoreNotInitialized = errors.New("the container storage was never initialized")
diff --git a/libpod/healthcheck_linux.go b/libpod/healthcheck_linux.go
index 08f37d412..b0f1ff35d 100644
--- a/libpod/healthcheck_linux.go
+++ b/libpod/healthcheck_linux.go
@@ -35,9 +35,8 @@ func (c *Container) createTimer() error {
conn.Close()
logrus.Debugf("creating systemd-transient files: %s %s", "systemd-run", cmd)
systemdRun := exec.Command("systemd-run", cmd...)
- _, err = systemdRun.CombinedOutput()
- if err != nil {
- return err
+ if output, err := systemdRun.CombinedOutput(); err != nil {
+ return errors.Errorf("%s", output)
}
return nil
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5dfb33afb..f5bf47694 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -410,48 +410,92 @@ func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, erro
if inputName == "" {
return "", nil, errors.Errorf("input name is blank")
}
+
// Check if the input name has a transport and if so strip it
dest, err := alltransports.ParseImageName(inputName)
if err == nil && dest.DockerReference() != nil {
inputName = dest.DockerReference().String()
}
- img, err := ir.getImage(stripSha256(inputName))
+ // Early check for fully-qualified images and (short) IDs.
+ img, err := ir.store.Image(stripSha256(inputName))
if err == nil {
- return inputName, img, err
+ return inputName, img, nil
}
- // container-storage wasn't able to find it in its current form
- // check if the input name has a tag, and if not, run it through
- // again
+ // Note that it's crucial to first decompose the image and check if
+ // it's a fully-qualified one or a "short name". The latter requires
+ // some normalization with search registries and the
+ // "localhost/prefix".
decomposedImage, err := decompose(inputName)
if err != nil {
+ // We may have a storage reference. We can't parse it to a
+ // reference before. Otherwise, we'd normalize "alpine" to
+ // "docker.io/library/alpine:latest" which would break the
+ // order in which we should query local images below.
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, err
}
- // The image has a registry name in it and we made sure we looked for it locally
- // with a tag. It cannot be local.
+ // The specified image is fully qualified, so it doesn't exist in the
+ // storage.
if decomposedImage.hasRegistry {
+ // However ... we may still need to normalize to docker.io:
+ // `docker.io/foo` -> `docker.io/library/foo`
+ if ref, err := is.Transport.ParseStoreReference(ir.store, inputName); err == nil {
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
+ }
return "", nil, errors.Wrapf(ErrNoSuchImage, imageError)
}
- // if the image is saved with the repository localhost, searching with localhost prepended is necessary
- // We don't need to strip the sha because we have already determined it is not an ID
- ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry)
+
+ // "Short-name image", so let's try out certain prefixes:
+ // 1) DefaultLocalRegistry (i.e., "localhost/)
+ // 2) Unqualified-search registries from registries.conf
+ unqualifiedSearchRegistries, err := registries.GetRegistries()
if err != nil {
return "", nil, err
}
- img, err = ir.getImage(ref.String())
+
+ for _, candidate := range append([]string{DefaultLocalRegistry}, unqualifiedSearchRegistries...) {
+ ref, err := decomposedImage.referenceWithRegistry(candidate)
+ if err != nil {
+ return "", nil, err
+ }
+ img, err := ir.store.Image(ref.String())
+ if err == nil {
+ return ref.String(), img, nil
+ }
+ }
+
+ // Backwards compat: normalize to docker.io as some users may very well
+ // rely on that.
+ ref, err := is.Transport.ParseStoreReference(ir.store, inputName)
if err == nil {
- return inputName, img, err
+ img, err = is.Transport.GetStoreImage(ir.store, ref)
+ if err == nil {
+ return inputName, img, nil
+ }
}
- // grab all the local images
+ // Last resort: look at the repotags of all images and try to find a
+ // match.
images, err := ir.GetImages()
if err != nil {
return "", nil, err
}
- // check the repotags of all images for a match
+ decomposedImage, err = decompose(inputName)
+ if err != nil {
+ return "", nil, err
+ }
repoImage, err := findImageInRepotags(decomposedImage, images)
if err == nil {
return inputName, repoImage, nil
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 2877191e5..21e43ebd0 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -25,7 +25,7 @@ import (
// Built from ../contrib/rootless-cni-infra.
var rootlessCNIInfraImage = map[string]string{
- "amd64": "quay.io/libpod/rootless-cni-infra@sha256:e92c3a6367f8e554121b96d39af1f19f0f9ac5a32922b290112e13bc661d3a29", // 2-amd64
+ "amd64": "quay.io/libpod/rootless-cni-infra@sha256:304742d5d221211df4ec672807a5842ff11e3729c50bc424ea0cea858f69d7b7", // 3-amd64
}
const (
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index e20d48d86..4a1196c89 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -6,7 +6,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -92,7 +92,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
// Perform HTTP attach.
@@ -109,7 +109,7 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 1d0b4c45d..0579da8de 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -82,7 +82,13 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
}
}
- workDir := "/"
+ workDir, err := newImage.WorkingDir(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ if workDir == "" {
+ workDir = "/"
+ }
if len(input.WorkingDir) > 0 {
workDir = input.WorkingDir
}
@@ -169,6 +175,11 @@ func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input
// away incorrectly formatted variables so we cannot reuse the
// parsing of the env input
// [Foo Other=one Blank=]
+ imgEnv, err := newImage.Env(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ input.Env = append(imgEnv, input.Env...)
for _, e := range input.Env {
splitEnv := strings.Split(e, "=")
switch len(splitEnv) {
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index 1db950f85..df51293c2 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -10,7 +10,7 @@ import (
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/containers/podman/v2/pkg/specgen/generate"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@@ -174,7 +174,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+ idleTracker := r.Context().Value("idletracker").(*idle.Tracker)
hijackChan := make(chan bool, 1)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan); err != nil {
@@ -186,7 +186,7 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
// We do need to tell the idle tracker that the
// connection has been closed, though. We can guarantee
// that is true after HTTPAttach exits.
- idleTracker.TrackHijackedClosed()
+ idleTracker.Close()
} else {
// A hijack was not successfully completed. We need to
// report the error normally.
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 0ce70975d..d5ccf56fe 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -60,37 +60,38 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
}()
query := struct {
+ BuildArgs string `schema:"buildargs"`
+ CacheFrom string `schema:"cachefrom"`
+ CpuPeriod uint64 `schema:"cpuperiod"` // nolint
+ CpuQuota int64 `schema:"cpuquota"` // nolint
+ CpuSetCpus string `schema:"cpusetcpus"` // nolint
+ CpuShares uint64 `schema:"cpushares"` // nolint
Dockerfile string `schema:"dockerfile"`
- Tag []string `schema:"t"`
ExtraHosts string `schema:"extrahosts"`
- Remote string `schema:"remote"`
- Quiet bool `schema:"q"`
+ ForceRm bool `schema:"forcerm"`
+ HTTPProxy bool `schema:"httpproxy"`
+ Labels string `schema:"labels"`
+ MemSwap int64 `schema:"memswap"`
+ Memory int64 `schema:"memory"`
+ NetworkMode string `schema:"networkmode"`
NoCache bool `schema:"nocache"`
- CacheFrom string `schema:"cachefrom"`
+ Outputs string `schema:"outputs"`
+ Platform string `schema:"platform"`
Pull bool `schema:"pull"`
+ Quiet bool `schema:"q"`
+ Registry string `schema:"registry"`
+ Remote string `schema:"remote"`
Rm bool `schema:"rm"`
- ForceRm bool `schema:"forcerm"`
- Memory int64 `schema:"memory"`
- MemSwap int64 `schema:"memswap"`
- CpuShares uint64 `schema:"cpushares"` // nolint
- CpuSetCpus string `schema:"cpusetcpus"` // nolint
- CpuPeriod uint64 `schema:"cpuperiod"` // nolint
- CpuQuota int64 `schema:"cpuquota"` // nolint
- BuildArgs string `schema:"buildargs"`
ShmSize int `schema:"shmsize"`
Squash bool `schema:"squash"`
- Labels string `schema:"labels"`
- NetworkMode string `schema:"networkmode"`
- Platform string `schema:"platform"`
+ Tag []string `schema:"t"`
Target string `schema:"target"`
- Outputs string `schema:"outputs"`
- Registry string `schema:"registry"`
}{
Dockerfile: "Dockerfile",
- Tag: []string{},
+ Registry: "docker.io",
Rm: true,
ShmSize: 64 * 1024 * 1024,
- Registry: "docker.io",
+ Tag: []string{},
}
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -186,6 +187,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
CPUQuota: query.CpuQuota,
CPUShares: query.CpuShares,
CPUSetCPUs: query.CpuSetCpus,
+ HTTPProxy: query.HTTPProxy,
Memory: query.Memory,
MemorySwap: query.MemSwap,
ShmSize: strconv.Itoa(query.ShmSize),
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 3ebf6e4c6..3054922c2 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -560,24 +560,41 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
func UntagImage(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- name := utils.GetName(r)
- newImage, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
- return
- }
- tag := "latest"
- if len(r.Form.Get("tag")) > 0 {
- tag = r.Form.Get("tag")
- }
- if len(r.Form.Get("repo")) < 1 {
+ tags := []string{} // Note: if empty, all tags will be removed from the image.
+ repo := r.Form.Get("repo")
+ tag := r.Form.Get("tag")
+
+ // Do the parameter dance.
+ switch {
+ // If tag is set, repo must be as well.
+ case len(repo) == 0 && len(tag) > 0:
utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
return
+
+ case len(repo) == 0:
+ break
+
+ // If repo is specified, we need to add that to the tags.
+ default:
+ if len(tag) == 0 {
+ // Normalize tag to "latest" if empty.
+ tag = "latest"
+ }
+ tags = append(tags, fmt.Sprintf("%s:%s", repo, tag))
}
- repo := r.Form.Get("repo")
- tagName := fmt.Sprintf("%s:%s", repo, tag)
- if err := newImage.UntagImage(tagName); err != nil {
- utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+
+ // Now use the ABI implementation to prevent us from having duplicate
+ // code.
+ opts := entities.ImageUntagOptions{}
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+
+ name := utils.GetName(r)
+ if err := imageEngine.Untag(r.Context(), name, tags, opts); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchImage {
+ utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
+ } else {
+ utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+ }
return
}
utils.WriteResponse(w, http.StatusCreated, "")
diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go
index 8e65248e2..2031dd42f 100644
--- a/pkg/api/handlers/libpod/manifests.go
+++ b/pkg/api/handlers/libpod/manifests.go
@@ -6,11 +6,13 @@ import (
"github.com/containers/buildah/manifests"
copy2 "github.com/containers/image/v5/copy"
+ "github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/api/handlers"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
+ "github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -48,17 +50,18 @@ func ManifestCreate(w http.ResponseWriter, r *http.Request) {
func ManifestInspect(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
name := utils.GetName(r)
- newImage, err := runtime.ImageRuntime().NewFromLocal(name)
- if err != nil {
- utils.ImageNotFound(w, name, err)
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ inspectReport, inspectError := imageEngine.ManifestInspect(r.Context(), name)
+ if inspectError != nil {
+ utils.Error(w, "Something went wrong.", http.StatusNotFound, inspectError)
return
}
- data, err := newImage.InspectManifest()
- if err != nil {
- utils.InternalServerError(w, err)
+ var list manifest.Schema2List
+ if err := json.Unmarshal(inspectReport, &list); err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Unmarshal()"))
return
}
- utils.WriteResponse(w, http.StatusOK, data)
+ utils.WriteResponse(w, http.StatusOK, &list)
}
func ManifestAdd(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/networks.go b/pkg/api/handlers/libpod/networks.go
index dfece2a4e..b3c4840b8 100644
--- a/pkg/api/handlers/libpod/networks.go
+++ b/pkg/api/handlers/libpod/networks.go
@@ -92,8 +92,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
}
if reports[0].Err != nil {
// If the network cannot be found, we return a 404.
- if errors.Cause(err) == define.ErrNoSuchNetwork {
- utils.Error(w, "Something went wrong", http.StatusNotFound, err)
+ if errors.Cause(reports[0].Err) == define.ErrNoSuchNetwork {
+ utils.Error(w, "Something went wrong", http.StatusNotFound, reports[0].Err)
return
}
}
diff --git a/pkg/api/server/idle/tracker.go b/pkg/api/server/idle/tracker.go
new file mode 100644
index 000000000..1b378c492
--- /dev/null
+++ b/pkg/api/server/idle/tracker.go
@@ -0,0 +1,96 @@
+package idle
+
+import (
+ "net"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/sirupsen/logrus"
+)
+
+// Tracker holds the state for the server's idle tracking
+type Tracker struct {
+ // Duration is the API idle window
+ Duration time.Duration
+ hijacked int // count of active connections managed by handlers
+ managed map[net.Conn]struct{} // set of active connections managed by http package
+ mux sync.Mutex // protect managed map
+ timer *time.Timer
+ total int // total number of connections made to this server instance
+}
+
+// NewTracker creates and initializes a new Tracker object
+// For best behavior, duration should be 2x http idle connection timeout
+func NewTracker(idle time.Duration) *Tracker {
+ return &Tracker{
+ managed: make(map[net.Conn]struct{}),
+ Duration: idle,
+ timer: time.NewTimer(idle),
+ }
+}
+
+// ConnState is called on HTTP connection state changes.
+// - Once StateHijacked, StateClose is _NOT_ called on that connection
+// - There are two "idle" timeouts, the http idle connection (not to be confused with the TCP/IP idle socket timeout)
+// and the API idle window. The caller should set the http idle timeout to 2x the time provided to NewTacker() which
+// is the API idle window.
+func (t *Tracker) ConnState(conn net.Conn, state http.ConnState) {
+ t.mux.Lock()
+ defer t.mux.Unlock()
+
+ logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections())
+ switch state {
+ case http.StateNew, http.StateActive:
+ // stop the API timer when the server transitions any connection to an "active" state
+ t.managed[conn] = struct{}{}
+ t.timer.Stop()
+ t.total++
+ case http.StateHijacked:
+ // hijacked connections should call Close() when finished.
+ // Note: If a handler hijack's a connection and then doesn't Close() it,
+ // the API timer will not fire and the server will _NOT_ timeout.
+ delete(t.managed, conn)
+ t.hijacked++
+ case http.StateIdle:
+ // When any connection goes into the http idle state, we know:
+ // - we have an active connection
+ // - the API timer should not be counting down (See case StateNew/StateActive)
+ break
+ case http.StateClosed:
+ oldActive := t.ActiveConnections()
+
+ // Either the server or a hijacking handler has closed the http connection to a client
+ if _, found := t.managed[conn]; found {
+ delete(t.managed, conn)
+ } else {
+ t.hijacked-- // guarded by t.mux above
+ }
+
+ // Transitioned from any "active" connection to no connections
+ if oldActive > 0 && t.ActiveConnections() == 0 {
+ t.timer.Stop() // See library source for Reset() issues and why they are not fixed
+ t.timer.Reset(t.Duration) // Restart the API window timer
+ }
+ }
+}
+
+// Close is used to update Tracker that a StateHijacked connection has been closed by handler (StateClosed)
+func (t *Tracker) Close() {
+ t.ConnState(nil, http.StateClosed)
+}
+
+// ActiveConnections returns the number of current managed or StateHijacked connections
+func (t *Tracker) ActiveConnections() int {
+ return len(t.managed) + t.hijacked
+}
+
+// TotalConnections returns total number of connections made to this instance of the service
+func (t *Tracker) TotalConnections() int {
+ return t.total
+}
+
+// Done is called when idle timer has expired
+func (t *Tracker) Done() <-chan time.Time {
+ return t.timer.C
+}
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
deleted file mode 100644
index 1ee905a99..000000000
--- a/pkg/api/server/idletracker/idletracker.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package idletracker
-
-import (
- "net"
- "net/http"
- "sync"
- "time"
-
- "github.com/sirupsen/logrus"
-)
-
-type IdleTracker struct {
- http map[net.Conn]struct{}
- hijacked int
- total int
- mux sync.Mutex
- timer *time.Timer
- Duration time.Duration
-}
-
-func NewIdleTracker(idle time.Duration) *IdleTracker {
- return &IdleTracker{
- http: make(map[net.Conn]struct{}),
- Duration: idle,
- timer: time.NewTimer(idle),
- }
-}
-
-func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- oldActive := t.ActiveConnections()
- logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, oldActive, t.TotalConnections())
- switch state {
- case http.StateNew, http.StateActive:
- t.http[conn] = struct{}{}
- // stop the timer if we transitioned from idle
- if oldActive == 0 {
- t.timer.Stop()
- }
- t.total++
- case http.StateHijacked:
- // hijacked connections are handled elsewhere
- delete(t.http, conn)
- t.hijacked++
- case http.StateIdle, http.StateClosed:
- delete(t.http, conn)
- // Restart the timer if we've become idle
- if oldActive > 0 && len(t.http) == 0 {
- t.timer.Stop()
- t.timer.Reset(t.Duration)
- }
- }
-}
-
-func (t *IdleTracker) TrackHijackedClosed() {
- t.mux.Lock()
- defer t.mux.Unlock()
-
- t.hijacked--
-}
-
-func (t *IdleTracker) ActiveConnections() int {
- return len(t.http) + t.hijacked
-}
-
-func (t *IdleTracker) TotalConnections() int {
- return t.total
-}
-
-func (t *IdleTracker) Done() <-chan time.Time {
- return t.timer.C
-}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index b1007fe09..cb0d26d1e 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1175,7 +1175,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// tags:
// - images
// summary: Untag an image
- // description: Untag an image
+ // description: Untag an image. If not repo and tag are specified, all tags are removed from the image.
// parameters:
// - in: path
// name: name:.*
@@ -1423,6 +1423,13 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: |
// output configuration TBD
// (As of version 1.xx)
+ // - in: query
+ // name: httpproxy
+ // type: boolean
+ // default:
+ // description: |
+ // Inject http proxy environment variables into container
+ // (As of version 2.0.0)
// produces:
// - application/json
// responses:
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index e7c031234..09a9f6370 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -16,7 +16,7 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers"
- "github.com/containers/podman/v2/pkg/api/server/idletracker"
+ "github.com/containers/podman/v2/pkg/api/server/idle"
"github.com/coreos/go-systemd/v22/activation"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/gorilla/mux"
@@ -26,14 +26,14 @@ import (
)
type APIServer struct {
- http.Server // The HTTP work happens here
- *schema.Decoder // Decoder for Query parameters to structs
- context.Context // Context to carry objects to handlers
- *libpod.Runtime // Where the real work happens
- net.Listener // mux for routing HTTP API calls to libpod routines
- context.CancelFunc // Stop APIServer
- idleTracker *idletracker.IdleTracker // Track connections to support idle shutdown
- pprof *http.Server // Sidecar http server for providing performance data
+ http.Server // The HTTP work happens here
+ *schema.Decoder // Decoder for Query parameters to structs
+ context.Context // Context to carry objects to handlers
+ *libpod.Runtime // Where the real work happens
+ net.Listener // mux for routing HTTP API calls to libpod routines
+ context.CancelFunc // Stop APIServer
+ idleTracker *idle.Tracker // Track connections to support idle shutdown
+ pprof *http.Server // Sidecar http server for providing performance data
}
// Number of seconds to wait for next request, if exceeded shutdown server
@@ -70,13 +70,13 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := idletracker.NewIdleTracker(duration)
+ idle := idle.NewTracker(duration)
server := APIServer{
Server: http.Server{
Handler: router,
ReadHeaderTimeout: 20 * time.Second,
- IdleTimeout: duration,
+ IdleTimeout: duration * 2,
ConnState: idle.ConnState,
ErrorLog: log.New(logrus.StandardLogger().Out, "", 0),
},
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index f52ba4e72..60ffea548 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -61,7 +61,7 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
params.Set("cpushares", strconv.Itoa(int(cpuShares)))
}
if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 {
- params.Set("cpusetcpues", cpuSetCpus)
+ params.Set("cpusetcpus", cpuSetCpus)
}
if cpuPeriod := options.CommonBuildOpts.CPUPeriod; cpuPeriod > 0 {
params.Set("cpuperiod", strconv.Itoa(int(cpuPeriod)))
@@ -93,6 +93,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
}
params.Set("labels", l)
}
+ if options.CommonBuildOpts.HTTPProxy {
+ params.Set("httpproxy", "1")
+ }
var (
headers map[string]string
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index f105dc333..803a59932 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -78,6 +78,6 @@ type ContainerEngine interface {
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
- VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
+ VolumePrune(ctx context.Context) ([]*VolumePruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
}
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 53d30ffdf..fb8466d04 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -113,10 +113,6 @@ type VolumeInspectReport struct {
*VolumeConfigResponse
}
-type VolumePruneOptions struct {
- Force bool
-}
-
type VolumePruneReport struct {
Err error
Id string //nolint
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go
index 672d0a69f..6c518e678 100644
--- a/pkg/domain/infra/abi/manifest.go
+++ b/pkg/domain/infra/abi/manifest.go
@@ -3,6 +3,7 @@
package abi
import (
+ "bytes"
"context"
"encoding/json"
"fmt"
@@ -11,15 +12,17 @@ import (
"strings"
"github.com/containers/buildah/manifests"
+ buildahManifests "github.com/containers/buildah/pkg/manifests"
+ "github.com/containers/buildah/util"
buildahUtil "github.com/containers/buildah/util"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
libpodImage "github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/domain/entities"
- "github.com/containers/podman/v2/pkg/util"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -41,28 +44,82 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin
// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) {
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- _, err := alltransports.ParseImageName(name)
+ if newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name); err == nil {
+ // return the manifest in local storage
+ if list, err := newImage.InspectManifest(); err == nil {
+ buf, err := json.MarshalIndent(list, "", " ")
+ if err != nil {
+ return buf, errors.Wrapf(err, "error rendering manifest %s for display", name)
+ }
+ return buf, nil
+ // no return if local image is not a list of images type
+ // continue on getting valid manifest through remote serice
+ } else if errors.Cause(err) != buildahManifests.ErrManifestTypeNotSupported {
+ return nil, errors.Wrapf(err, "loading manifest %q", name)
+ }
+ }
+ sc := ir.Libpod.SystemContext()
+ refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name)
if err != nil {
- _, err = alltransports.ParseImageName(dockerPrefix + name)
+ return nil, err
+ }
+ var (
+ latestErr error
+ result []byte
+ manType string
+ b bytes.Buffer
+ )
+ appendErr := func(e error) {
+ if latestErr == nil {
+ latestErr = e
+ } else {
+ latestErr = errors.Wrapf(latestErr, "tried %v\n", e)
+ }
+ }
+ for _, ref := range refs {
+ src, err := ref.NewImageSource(ctx, sc)
+ if err != nil {
+ appendErr(errors.Wrapf(err, "reading image %q", transports.ImageName(ref)))
+ continue
+ }
+ defer src.Close()
+
+ manifestBytes, manifestType, err := src.GetManifest(ctx, nil)
if err != nil {
- return nil, errors.Errorf("invalid image reference %q", name)
+ appendErr(errors.Wrapf(err, "loading manifest %q", transports.ImageName(ref)))
+ continue
}
+
+ if !manifest.MIMETypeIsMultiImage(manifestType) {
+ appendErr(errors.Errorf("manifest is of type %s (not a list type)", manifestType))
+ continue
+ }
+ result = manifestBytes
+ manType = manifestType
+ break
}
- image, err := ir.Libpod.ImageRuntime().New(ctx, name, "", "", nil, nil, libpodImage.SigningOptions{}, nil, util.PullImageMissing)
- if err != nil {
- return nil, errors.Wrapf(err, "reading image %q", name)
+ if len(result) == 0 && latestErr != nil {
+ return nil, latestErr
}
+ if manType != manifest.DockerV2ListMediaType {
+ listBlob, err := manifest.ListFromBlob(result, manType)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing manifest blob %q as a %q", string(result), manType)
+ }
+ list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType)
+ if err != nil {
+ return nil, err
+ }
+ if result, err = list.Serialize(); err != nil {
+ return nil, err
+ }
- list, err := image.InspectManifest()
- if err != nil {
- return nil, errors.Wrapf(err, "loading manifest %q", name)
}
- buf, err := json.MarshalIndent(list, "", " ")
+ err = json.Indent(&b, result, "", " ")
if err != nil {
- return buf, errors.Wrapf(err, "error rendering manifest for display")
+ return nil, errors.Wrapf(err, "error rendering manifest %s for display", name)
}
- return buf, nil
+ return b.Bytes(), nil
}
// ManifestAdd adds images to the manifest list
diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go
index 053be6528..5acfea853 100644
--- a/pkg/domain/infra/abi/network.go
+++ b/pkg/domain/infra/abi/network.go
@@ -12,6 +12,7 @@ import (
"github.com/containernetworking/cni/libcni"
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containers/podman/v2/libpod"
+ "github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/network"
"github.com/containers/podman/v2/pkg/util"
@@ -85,7 +86,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
// if user passes force, we nuke containers and pods
if !options.Force {
// Without the force option, we return an error
- return reports, errors.Errorf("%q has associated containers with it. Use -f to forcibly delete containers and pods", name)
+ return reports, errors.Wrapf(define.ErrNetworkInUse, "%q has associated containers with it. Use -f to forcibly delete containers and pods", name)
}
if c.IsInfra() {
// if we have a infra container we need to remove the pod
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 340f00953..946f258af 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -120,7 +120,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
return reports, nil
}
-func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
return ic.pruneVolumesHelper(ctx)
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 981884109..61ac2141c 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -9,7 +9,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
- "github.com/containers/podman/v2/pkg/bindings"
images "github.com/containers/podman/v2/pkg/bindings/images"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/utils"
@@ -139,13 +138,8 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string,
}
func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error {
- // Remove all tags if none are provided
if len(tags) == 0 {
- newImage, err := images.GetImage(ir.ClientCxt, nameOrID, bindings.PFalse)
- if err != nil {
- return err
- }
- tags = newImage.NamesHistory
+ return images.Untag(ir.ClientCxt, nameOrID, "", "")
}
for _, newTag := range tags {
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index ee2786330..e432d3292 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -56,7 +56,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
return reports, nil
}
-func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
+func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) {
return volumes.Prune(ic.ClientCxt)
}
diff --git a/pkg/registries/registries.go b/pkg/registries/registries.go
index 5dff25c7d..949c5d835 100644
--- a/pkg/registries/registries.go
+++ b/pkg/registries/registries.go
@@ -1,5 +1,10 @@
package registries
+// TODO: this package should not exist anymore. Users should either use
+// c/image's `sysregistriesv2` package directly OR, even better, we cache a
+// config in libpod's image runtime so we don't need to parse the
+// registries.conf files redundantly.
+
import (
"os"
"path/filepath"
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index d03663f12..319cce61f 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -200,6 +200,9 @@ func getDevices(path string) ([]*configs.Device, error) {
}
case f.Name() == "console":
continue
+ case f.Mode()&os.ModeSymlink != 0:
+ // do not add symlink'd devices to privileged devices
+ continue
}
device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 42228540c..81620997f 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -182,14 +182,23 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
g.SetProcessCwd(config.WorkDir)
ProcessArgs := make([]string, 0)
- if len(config.Entrypoint) > 0 {
- ProcessArgs = config.Entrypoint
+ // We need to iterate the input for entrypoint because it is a []string
+ // but "" is a legit json input, which translates into a []string with an
+ // empty position. This messes up the eventual command being executed
+ // in the container
+ for _, a := range config.Entrypoint {
+ if len(a) > 0 {
+ ProcessArgs = append(ProcessArgs, a)
+ }
}
- if len(config.Command) > 0 {
- ProcessArgs = append(ProcessArgs, config.Command...)
+ // Same issue as explained above for config.Entrypoint.
+ for _, a := range config.Command {
+ if len(a) > 0 {
+ ProcessArgs = append(ProcessArgs, a)
+ }
}
- g.SetProcessArgs(ProcessArgs)
+ g.SetProcessArgs(ProcessArgs)
g.SetProcessTerminal(config.Tty)
for key, val := range config.Annotations {
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 147ebd61b..2ee8f2441 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -13,6 +13,7 @@ import (
"github.com/containers/podman/v2/pkg/specgen"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@@ -33,7 +34,43 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
_, mediaType, err := newImage.Manifest(ctx)
if err != nil {
- return nil, err
+ if errors.Cause(err) != image.ErrImageIsBareList {
+ return nil, err
+ }
+ // if err is not runnable image
+ // use the local store image with repo@digest matches with the list, if exists
+ manifestByte, manifestType, err := newImage.GetManifest(ctx, nil)
+ if err != nil {
+ return nil, err
+ }
+ list, err := manifest.ListFromBlob(manifestByte, manifestType)
+ if err != nil {
+ return nil, err
+ }
+ images, err := r.ImageRuntime().GetImages()
+ if err != nil {
+ return nil, err
+ }
+ findLocal := false
+ listDigest, err := list.ChooseInstance(r.SystemContext())
+ if err != nil {
+ return nil, err
+ }
+ for _, img := range images {
+ for _, imageDigest := range img.Digests() {
+ if imageDigest == listDigest {
+ newImage = img
+ s.Image = img.ID()
+ mediaType = manifestType
+ findLocal = true
+ logrus.Debug("image contains manifest list, using image from local storage")
+ break
+ }
+ }
+ }
+ if !findLocal {
+ return nil, image.ErrImageIsBareList
+ }
}
if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
@@ -75,8 +112,8 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
if err != nil {
return nil, errors.Wrap(err, "error parsing fields in containers.conf")
}
- if defaultEnvs["containers"] == "" {
- defaultEnvs["containers"] = "podman"
+ if defaultEnvs["container"] == "" {
+ defaultEnvs["container"] = "podman"
}
var envs map[string]string
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 7c818cf62..d17cd4a9a 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -131,12 +131,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
configSpec := g.Config
+ configSpec.Process.Capabilities.Ambient = []string{}
configSpec.Process.Capabilities.Bounding = caplist
+ configSpec.Process.Capabilities.Inheritable = caplist
if s.User == "" || s.User == "root" || s.User == "0" {
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
- configSpec.Process.Capabilities.Inheritable = caplist
} else {
userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd)
if err != nil {
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index 187073fb9..15b5dc4be 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -3,8 +3,11 @@
# test container-related endpoints
#
-podman pull $IMAGE &>/dev/null
+# WORKDIR=/data
+ENV_WORKDIR_IMG=docker.io/library/redis:alpine
+podman pull $IMAGE &>/dev/null
+podman pull $ENV_WORKDIR_IMG &>/dev/null
# Unimplemented
#t POST libpod/containers/create '' 201 'sdf'
@@ -203,4 +206,22 @@ t POST containers/${cid_top}/stop "" 204
t DELETE containers/$cid 204
t DELETE containers/$cid_top 204
+# test the apiv2 create, should't ignore the ENV and WORKDIR from the image
+t POST containers/create '"Image":"'$ENV_WORKDIR_IMG'","Env":["testKey1"]' 201 \
+ .Id~[0-9a-f]\\{64\\}
+cid=$(jq -r '.Id' <<<"$output")
+t GET containers/$cid/json 200 \
+ .Config.Env~"REDIS_VERSION=" \
+ .Config.Env~"testEnv1=" \
+ .Config.WorkingDir="/data" # default is /data
+t DELETE containers/$cid 204
+
+# test the WORKDIR
+t POST containers/create '"Image":"'$ENV_WORKDIR_IMG'","WorkingDir":"/dataDir"' 201 \
+ .Id~[0-9a-f]\\{64\\}
+cid=$(jq -r '.Id' <<<"$output")
+t GET containers/$cid/json 200 \
+ .Config.WorkingDir="/dataDir"
+t DELETE containers/$cid 204
+
# vim: filetype=sh
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 93186bc8b..f22a4c3af 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -27,7 +27,7 @@ var _ = Describe("Podman checkpoint", func() {
)
BeforeEach(func() {
- SkipIfRootless() //checkpoint not supported in rootless mode
+ SkipIfRootless("checkpoint not supported in rootless mode")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 1943020c3..c663a4dca 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -599,6 +599,26 @@ func (p *PodmanTestIntegration) CreateSeccompJson(in []byte) (string, error) {
return jsonFile, nil
}
+func checkReason(reason string) {
+ if len(reason) < 5 {
+ panic("Test must specify a reason to skip")
+ }
+}
+
+func SkipIfRootlessCgroupsV1(reason string) {
+ checkReason(reason)
+ if os.Geteuid() != 0 && !CGROUPSV2 {
+ Skip("[rootless]: " + reason)
+ }
+}
+
+func SkipIfRootless(reason string) {
+ checkReason(reason)
+ if os.Geteuid() != 0 {
+ ginkgo.Skip("[rootless]: " + reason)
+ }
+}
+
func SkipIfNotFedora() {
info := GetHostDistributionInfo()
if info.Distribution != "fedora" {
@@ -610,21 +630,32 @@ func isRootless() bool {
return os.Geteuid() != 0
}
-func SkipIfCgroupV1() {
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
+func SkipIfCgroupV1(reason string) {
+ checkReason(reason)
+ if !CGROUPSV2 {
+ Skip(reason)
+ }
+}
- if !cgroupsv2 {
- Skip("Skip on systems with cgroup V1 systems")
+func SkipIfCgroupV2(reason string) {
+ checkReason(reason)
+ if CGROUPSV2 {
+ Skip(reason)
}
}
-func SkipIfCgroupV2() {
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
+func isContainerized() bool {
+ // This is set to "podman" by podman automatically
+ if os.Getenv("container") != "" {
+ return true
+ }
+ return false
+}
- if cgroupsv2 {
- Skip("Skip on systems with cgroup V2 systems")
+func SkipIfContainerized(reason string) {
+ checkReason(reason)
+ if isContainerized() {
+ Skip(reason)
}
}
diff --git a/test/e2e/config.go b/test/e2e/config.go
index 0e1850614..49a47c7da 100644
--- a/test/e2e/config.go
+++ b/test/e2e/config.go
@@ -23,8 +23,4 @@ var (
// This image has a bogus/invalid seccomp profile which should
// yield a json error when being read.
alpineBogusSeccomp = "docker.io/libpod/alpine-with-bogus-seccomp:label"
-
- // v2fail is a temporary variable to help us track
- // tests that fail in v2
- v2fail = "does not pass integration tests with v2 podman"
)
diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go
index ddb62c327..965e51973 100644
--- a/test/e2e/containers_conf_test.go
+++ b/test/e2e/containers_conf_test.go
@@ -41,7 +41,7 @@ var _ = Describe("Podman run", func() {
})
It("podman run limits test", func() {
- SkipIfRootlessCgroupsV1()
+ SkipIfRootlessCgroupsV1("Setting limits not supported on cgroupv1 for rootless users")
//containers.conf is set to "nofile=500:500"
session := podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "ulimit", "-n"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go
index a53485fa4..0a9fa990c 100644
--- a/test/e2e/cp_test.go
+++ b/test/e2e/cp_test.go
@@ -269,11 +269,11 @@ var _ = Describe("Podman cp", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"exec", "-u", "testuser", "testctr", "touch", "testfile"})
+ session = podmanTest.Podman([]string{"exec", "-u", "testuser", "testctr", "touch", "/tmp/testfile"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:testfile", "testfile1"})
+ session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:/tmp/testfile", "testfile1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go
index 57d1c3f2c..7a2267617 100644
--- a/test/e2e/create_staticip_test.go
+++ b/test/e2e/create_staticip_test.go
@@ -49,7 +49,7 @@ var _ = Describe("Podman create with --ip flag", func() {
})
It("Podman create --ip with non-allocatable IP", func() {
- SkipIfRootless() // --ip is not supported in rootless mode
+ SkipIfRootless("--ip is not supported in rootless mode")
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -81,7 +81,7 @@ var _ = Describe("Podman create with --ip flag", func() {
})
It("Podman create two containers with the same IP", func() {
- SkipIfRootless() // --ip not supported in rootless mode
+ SkipIfRootless("--ip not supported in rootless mode")
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"create", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index 45dbe9b56..96a234446 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -552,7 +552,7 @@ var _ = Describe("Podman create", func() {
})
It("create container in pod with IP should fail", func() {
- SkipIfRootless() //Setting IP not supported in rootless mode
+ SkipIfRootless("Setting IP not supported in rootless mode")
name := "createwithstaticip"
pod := podmanTest.RunTopContainerInPod("", "new:"+name)
pod.WaitWithDefaultTimeout()
@@ -564,7 +564,7 @@ var _ = Describe("Podman create", func() {
})
It("create container in pod with mac should fail", func() {
- SkipIfRootless() //Setting MAC Address not supported in rootless mode
+ SkipIfRootless("Setting MAC Address not supported in rootless mode")
name := "createwithstaticmac"
pod := podmanTest.RunTopContainerInPod("", "new:"+name)
pod.WaitWithDefaultTimeout()
@@ -609,4 +609,21 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).ToNot(BeZero())
})
+ It("create use local store image if input image contains a manifest list", func() {
+ session := podmanTest.Podman([]string{"pull", BB})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ session = podmanTest.Podman([]string{"manifest", "create", "mylist"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"manifest", "add", "--all", "mylist", BB})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ session = podmanTest.Podman([]string{"create", "mylist"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ })
})
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index 7d50c02b2..93a713f28 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -286,7 +286,7 @@ var _ = Describe("Podman exec", func() {
It("podman exec preserves container groups with --user and --group-add", func() {
SkipIfRemote("FIXME: This is broken SECCOMP Failues?")
- dockerfile := `FROM fedora-minimal
+ dockerfile := `FROM registry.fedoraproject.org/fedora-minimal
RUN groupadd -g 4000 first
RUN groupadd -g 4001 second
RUN useradd -u 1000 auser`
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 0a0b2799b..fa87302ee 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -24,16 +24,10 @@ func IsRemote() bool {
}
func SkipIfRemote(reason string) {
- ginkgo.Skip("[remote]: " + reason)
-}
-
-func SkipIfRootlessCgroupsV1() {
-}
-
-func SkipIfRootless() {
- if os.Geteuid() != 0 {
- ginkgo.Skip("This function is not enabled for rootless podman")
+ if len(reason) < 5 {
+ panic("SkipIfRemote must specify a reason to skip")
}
+ ginkgo.Skip("[remote]: " + reason)
}
// Podman is the exec call to podman on the filesystem
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index 00d066fea..a9da922de 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -8,8 +8,6 @@ import (
"os"
"path/filepath"
"strings"
-
- . "github.com/onsi/ginkgo"
)
func IsRemote() bool {
@@ -19,18 +17,6 @@ func IsRemote() bool {
func SkipIfRemote(string) {
}
-func SkipIfRootlessCgroupsV1() {
- if os.Geteuid() != 0 && !CGROUPSV2 {
- Skip("Rooless requires cgroupsV2 to set limits")
- }
-}
-
-func SkipIfRootless() {
- if os.Geteuid() != 0 {
- Skip("This function is not enabled for rootless podman")
- }
-}
-
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, false)
diff --git a/test/e2e/libpod_suite_varlink_test.go b/test/e2e/libpod_suite_varlink_test.go
index f901cbec9..275a1115e 100644
--- a/test/e2e/libpod_suite_varlink_test.go
+++ b/test/e2e/libpod_suite_varlink_test.go
@@ -23,19 +23,10 @@ func IsRemote() bool {
return true
}
-func SkipIfRootlessCgroupsV1() {
-}
-
func SkipIfRemote(reason string) {
ginkgo.Skip("[remote]: " + reason)
}
-func SkipIfRootless() {
- if os.Geteuid() != 0 {
- ginkgo.Skip("This function is not enabled for rootless podman")
- }
-}
-
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, false)
diff --git a/test/e2e/login_logout_test.go b/test/e2e/login_logout_test.go
index 52357b00a..b1255c00a 100644
--- a/test/e2e/login_logout_test.go
+++ b/test/e2e/login_logout_test.go
@@ -47,7 +47,7 @@ var _ = Describe("Podman login and logout", func() {
se := SystemExec("setenforce", []string{"0"})
se.WaitWithDefaultTimeout()
if se.ExitCode() != 0 {
- Skip("Can not disable selinux, this may cause problem for reading cert files inside container.")
+ Skip("Cannot disable selinux, this may cause problem for reading cert files inside container.")
}
defer SystemExec("setenforce", []string{"1"})
}
@@ -87,7 +87,7 @@ var _ = Describe("Podman login and logout", func() {
Expect(session.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
})
@@ -116,8 +116,6 @@ var _ = Describe("Podman login and logout", func() {
})
It("podman login and logout without registry parameter", func() {
- SkipIfRootless()
-
registriesConf, err := ioutil.TempFile("", "TestLoginWithoutParameter")
Expect(err).To(BeNil())
defer registriesConf.Close()
@@ -231,7 +229,7 @@ var _ = Describe("Podman login and logout", func() {
Expect(session.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry1", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", server})
diff --git a/test/e2e/manifest_test.go b/test/e2e/manifest_test.go
index 33aac48d5..b85132814 100644
--- a/test/e2e/manifest_test.go
+++ b/test/e2e/manifest_test.go
@@ -8,6 +8,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman manifest", func() {
@@ -49,6 +50,16 @@ var _ = Describe("Podman manifest", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman manifest inspect", func() {
+ session := podmanTest.Podman([]string{"manifest", "inspect", BB})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.PodmanNoCache([]string{"manifest", "inspect", "docker.io/library/busybox"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ })
+
It("podman manifest add", func() {
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/mount_test.go b/test/e2e/mount_test.go
index 4f60cc6df..4223961a6 100644
--- a/test/e2e/mount_test.go
+++ b/test/e2e/mount_test.go
@@ -18,7 +18,7 @@ var _ = Describe("Podman mount", func() {
)
BeforeEach(func() {
- SkipIfRootless()
+ SkipIfRootless("Podman mount requires podman unshare first to work")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -189,7 +189,6 @@ var _ = Describe("Podman mount", func() {
})
It("podman list running container", func() {
- SkipIfRootless() // FIXME: We need to do a podman unshare before executing this code.
setup := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"})
setup.WaitWithDefaultTimeout()
@@ -212,7 +211,6 @@ var _ = Describe("Podman mount", func() {
})
It("podman list multiple mounted containers", func() {
- SkipIfRootless() // FIXME: We need to do a podman unshare before executing this code.
setup := podmanTest.Podman([]string{"create", ALPINE, "ls"})
setup.WaitWithDefaultTimeout()
@@ -257,7 +255,6 @@ var _ = Describe("Podman mount", func() {
})
It("podman list mounted container", func() {
- SkipIfRootless() // FIXME: We need to do a podman unshare before executing this code.
setup := podmanTest.Podman([]string{"create", ALPINE, "ls"})
setup.WaitWithDefaultTimeout()
diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go
index f6d9f2cc3..edd76739f 100644
--- a/test/e2e/network_create_test.go
+++ b/test/e2e/network_create_test.go
@@ -58,7 +58,7 @@ func genericPluginsToPortMap(plugins interface{}, pluginType string) (network.Po
func (p *PodmanTestIntegration) removeCNINetwork(name string) {
session := p.Podman([]string{"network", "rm", "-f", name})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(BeZero())
+ Expect(session.ExitCode()).To(BeNumerically("<=", 1))
}
func removeNetworkDevice(name string) {
@@ -178,7 +178,7 @@ var _ = Describe("Podman network create", func() {
})
It("podman network create with name and IPv6 subnet", func() {
- SkipIfRootless() // FIXME I believe this should work in rootlessmode
+ SkipIfRootless("FIXME I believe this should work in rootlessmode")
var (
results []network.NcList
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index 2ea8291fc..a15359ea3 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -25,6 +25,42 @@ func removeConf(confPath string) {
}
}
+// generateNetworkConfig generates a cni config with a random name
+// it returns the network name and the filepath
+func generateNetworkConfig(p *PodmanTestIntegration) (string, string) {
+ // generate a random name to preven conflicts with other tests
+ name := "net" + stringid.GenerateNonCryptoID()
+ path := filepath.Join(p.CNIConfigDir, fmt.Sprintf("%s.conflist", name))
+ conf := fmt.Sprintf(`{
+ "cniVersion": "0.3.0",
+ "name": "%s",
+ "plugins": [
+ {
+ "type": "bridge",
+ "bridge": "cni1",
+ "isGateway": true,
+ "ipMasq": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.99.0.0/16",
+ "routes": [
+ { "dst": "0.0.0.0/0" }
+ ]
+ }
+ },
+ {
+ "type": "portmap",
+ "capabilities": {
+ "portMappings": true
+ }
+ }
+ ]
+ }`, name)
+ writeConf([]byte(conf), path)
+
+ return name, path
+}
+
var _ = Describe("Podman network", func() {
var (
tempdir string
@@ -48,84 +84,44 @@ var _ = Describe("Podman network", func() {
})
- var (
- secondConf = `{
- "cniVersion": "0.3.0",
- "name": "podman-integrationtest",
- "plugins": [
- {
- "type": "bridge",
- "bridge": "cni1",
- "isGateway": true,
- "ipMasq": true,
- "ipam": {
- "type": "host-local",
- "subnet": "10.99.0.0/16",
- "routes": [
- { "dst": "0.0.0.0/0" }
- ]
- }
- },
- {
- "type": "portmap",
- "capabilities": {
- "portMappings": true
- }
- }
- ]
-}`
- )
-
It("podman network list", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
session := podmanTest.Podman([]string{"network", "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue())
+ Expect(session.LineInOutputContains(name)).To(BeTrue())
})
It("podman network list -q", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
session := podmanTest.Podman([]string{"network", "ls", "--quiet"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue())
+ Expect(session.LineInOutputContains(name)).To(BeTrue())
})
It("podman network list --filter success", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue())
+ Expect(session.LineInOutputContains(name)).To(BeTrue())
})
It("podman network list --filter failure", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=test"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.LineInOutputContains("podman-integrationtest")).To(BeFalse())
+ Expect(session.LineInOutputContains(name)).To(BeFalse())
})
It("podman network rm no args", func() {
@@ -135,26 +131,23 @@ var _ = Describe("Podman network", func() {
})
It("podman network rm", func() {
- SkipIfRootless() // FIXME: This one is definitely broken in rootless mode
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ SkipIfRootless("FIXME: This one is definitely broken in rootless mode")
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
session := podmanTest.Podman([]string{"network", "ls", "--quiet"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- Expect(session.LineInOutputContains("podman-integrationtest")).To(BeTrue())
+ Expect(session.LineInOutputContains(name)).To(BeTrue())
- rm := podmanTest.Podman([]string{"network", "rm", "podman-integrationtest"})
+ rm := podmanTest.Podman([]string{"network", "rm", name})
rm.WaitWithDefaultTimeout()
Expect(rm.ExitCode()).To(BeZero())
results := podmanTest.Podman([]string{"network", "ls", "--quiet"})
results.WaitWithDefaultTimeout()
Expect(results.ExitCode()).To(Equal(0))
- Expect(results.LineInOutputContains("podman-integrationtest")).To(BeFalse())
+ Expect(results.LineInOutputContains(name)).To(BeFalse())
})
It("podman network inspect no args", func() {
@@ -164,13 +157,10 @@ var _ = Describe("Podman network", func() {
})
It("podman network inspect", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
- expectedNetworks := []string{"podman-integrationtest"}
+ expectedNetworks := []string{name}
if !rootless.IsRootless() {
// rootful image contains "podman/cni/87-podman-bridge.conflist" for "podman" network
expectedNetworks = append(expectedNetworks, "podman")
@@ -182,13 +172,10 @@ var _ = Describe("Podman network", func() {
})
It("podman network inspect", func() {
- // Setup, use uuid to prevent conflict with other tests
- uuid := stringid.GenerateNonCryptoID()
- secondPath := filepath.Join(podmanTest.CNIConfigDir, fmt.Sprintf("%s.conflist", uuid))
- writeConf([]byte(secondConf), secondPath)
- defer removeConf(secondPath)
+ name, path := generateNetworkConfig(podmanTest)
+ defer removeConf(path)
- session := podmanTest.Podman([]string{"network", "inspect", "podman-integrationtest", "--format", "{{.cniVersion}}"})
+ session := podmanTest.Podman([]string{"network", "inspect", name, "--format", "{{.cniVersion}}"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOutputContains("0.3.0")).To(BeTrue())
@@ -265,11 +252,18 @@ var _ = Describe("Podman network", func() {
Expect(rmAll.ExitCode()).To(BeZero())
})
+ It("podman network remove bogus", func() {
+ session := podmanTest.Podman([]string{"network", "rm", "bogus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
+
It("podman network remove --force with pod", func() {
netName := "testnet"
session := podmanTest.Podman([]string{"network", "create", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName)
session = podmanTest.Podman([]string{"pod", "create", "--network", netName})
session.WaitWithDefaultTimeout()
@@ -280,6 +274,10 @@ var _ = Describe("Podman network", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ session = podmanTest.Podman([]string{"network", "rm", netName})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(2))
+
session = podmanTest.Podman([]string{"network", "rm", "--force", netName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
@@ -301,11 +299,13 @@ var _ = Describe("Podman network", func() {
session := podmanTest.Podman([]string{"network", "create", netName1})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName1)
netName2 := "net2"
session = podmanTest.Podman([]string{"network", "create", netName2})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(BeZero())
+ defer podmanTest.removeCNINetwork(netName2)
session = podmanTest.Podman([]string{"network", "rm", netName1, netName2})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go
index a49304bbe..a90ffcc87 100644
--- a/test/e2e/pause_test.go
+++ b/test/e2e/pause_test.go
@@ -7,7 +7,6 @@ import (
"path/filepath"
"strings"
- "github.com/containers/podman/v2/pkg/cgroups"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -24,16 +23,13 @@ var _ = Describe("Podman pause", func() {
createdState := "created"
BeforeEach(func() {
- SkipIfRootlessCgroupsV1() // Pause is not supported in cgroups v1
+ SkipIfRootlessCgroupsV1("Pause is not supported in cgroups v1")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
- if cgroupsv2 {
+ if CGROUPSV2 {
b, err := ioutil.ReadFile("/proc/self/cgroup")
if err != nil {
Skip("cannot read self cgroup")
diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go
index 182d99d51..3dabf7b4a 100644
--- a/test/e2e/pod_pause_test.go
+++ b/test/e2e/pod_pause_test.go
@@ -18,7 +18,7 @@ var _ = Describe("Podman pod pause", func() {
pausedState := "paused"
BeforeEach(func() {
- SkipIfRootlessCgroupsV1() // Pause is not supported in cgroups v1
+ SkipIfRootlessCgroupsV1("Pause is not supported in cgroups v1")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go
index cb9b93a15..24643e6b2 100644
--- a/test/e2e/pod_rm_test.go
+++ b/test/e2e/pod_rm_test.go
@@ -195,8 +195,7 @@ var _ = Describe("Podman pod rm", func() {
It("podman rm bogus pod", func() {
session := podmanTest.Podman([]string{"pod", "rm", "bogus"})
session.WaitWithDefaultTimeout()
- // TODO: `podman rm` returns 1 for a bogus container. Should the RC be consistent?
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
})
It("podman rm bogus pod and a running pod", func() {
@@ -209,11 +208,11 @@ var _ = Describe("Podman pod rm", func() {
session = podmanTest.Podman([]string{"pod", "rm", "bogus", "test1"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
session = podmanTest.Podman([]string{"pod", "rm", "test1", "bogus"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
})
It("podman rm --ignore bogus pod and a running pod", func() {
diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go
index 02fb3bc57..1ffbe282b 100644
--- a/test/e2e/pod_stats_test.go
+++ b/test/e2e/pod_stats_test.go
@@ -17,7 +17,7 @@ var _ = Describe("Podman pod stats", func() {
BeforeEach(func() {
if os.Geteuid() != 0 {
- SkipIfCgroupV2()
+ SkipIfCgroupV2("--cgroup-manager=cgroupfs which doesn't work in rootless mode")
}
tempdir, err = CreateTempDirInTempDir()
@@ -175,7 +175,7 @@ var _ = Describe("Podman pod stats", func() {
It("podman stats on net=host post", func() {
// --net=host not supported for rootless pods at present
- SkipIfRootlessCgroupsV1() // Pause stats not supported in cgroups v1
+ SkipIfRootlessCgroupsV1("Pause stats not supported in cgroups v1")
podName := "testPod"
podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName})
podCreate.WaitWithDefaultTimeout()
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index aabec4f55..82a842146 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -206,7 +206,7 @@ var _ = Describe("Podman ps", func() {
})
It("podman ps namespace flag with go template format", func() {
- Skip(v2fail)
+ Skip("FIXME: table still not supported in podman ps command")
_, ec, _ := podmanTest.RunLsContainer("test1")
Expect(ec).To(Equal(0))
diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go
index 2280d16cc..edc17fdbf 100644
--- a/test/e2e/pull_test.go
+++ b/test/e2e/pull_test.go
@@ -1,9 +1,8 @@
package integration
import (
- "os"
-
"fmt"
+ "os"
"path/filepath"
"strings"
@@ -400,4 +399,101 @@ var _ = Describe("Podman pull", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+
+ It("podman pull + inspect from unqualified-search registry", func() {
+ // Regression test for #6381:
+ // Make sure that `pull shortname` and `inspect shortname`
+ // refer to the same image.
+
+ // We already tested pulling, so we can save some energy and
+ // just restore local artifacts and tag them.
+ podmanTest.RestoreArtifact(ALPINE)
+ podmanTest.RestoreArtifact(BB)
+
+ // What we want is at least two images which have the same name
+ // and are prefixed with two different unqualified-search
+ // registries from ../registries.conf.
+ //
+ // A `podman inspect $name` must yield the one from the _first_
+ // matching registry in the registries.conf.
+ getID := func(image string) string {
+ setup := podmanTest.PodmanNoCache([]string{"image", "inspect", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ return data[0].ID
+ }
+
+ untag := func(image string) {
+ setup := podmanTest.PodmanNoCache([]string{"untag", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ setup = podmanTest.PodmanNoCache([]string{"image", "inspect", image})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ Expect(len(data[0].RepoTags)).To(Equal(0))
+ }
+
+ tag := func(image, tag string) {
+ setup := podmanTest.PodmanNoCache([]string{"tag", image, tag})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+ setup = podmanTest.PodmanNoCache([]string{"image", "exists", tag})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+ }
+
+ image1 := getID(ALPINE)
+ image2 := getID(BB)
+
+ // $ head -n2 ../registries.conf
+ // [registries.search]
+ // registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
+ registries := []string{"docker.io", "quay.io", "registry.fedoraproject.org"}
+ name := "foo/test:tag"
+ tests := []struct {
+ // tag1 has precedence (see list above) over tag2 when
+ // doing an inspect on "test:tag".
+ tag1, tag2 string
+ }{
+ {
+ fmt.Sprintf("%s/%s", registries[0], name),
+ fmt.Sprintf("%s/%s", registries[1], name),
+ },
+ {
+ fmt.Sprintf("%s/%s", registries[0], name),
+ fmt.Sprintf("%s/%s", registries[2], name),
+ },
+ {
+ fmt.Sprintf("%s/%s", registries[1], name),
+ fmt.Sprintf("%s/%s", registries[2], name),
+ },
+ }
+
+ for _, t := range tests {
+ // 1) untag both images
+ // 2) tag them according to `t`
+ // 3) make sure that an inspect of `name` returns `image1` with `tag1`
+ untag(image1)
+ untag(image2)
+ tag(image1, t.tag1)
+ tag(image2, t.tag2)
+
+ setup := podmanTest.PodmanNoCache([]string{"image", "inspect", name})
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ data := setup.InspectImageJSON() // returns []inspect.ImageData
+ Expect(len(data)).To(Equal(1))
+ Expect(len(data[0].RepoTags)).To(Equal(1))
+ Expect(data[0].RepoTags[0]).To(Equal(t.tag1))
+ Expect(data[0].ID).To(Equal(image1))
+ }
+ })
})
diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go
index 9d2daaf9d..45b8769a2 100644
--- a/test/e2e/push_test.go
+++ b/test/e2e/push_test.go
@@ -70,7 +70,7 @@ var _ = Describe("Podman push", func() {
Expect(session.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
push := podmanTest.PodmanNoCache([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"})
@@ -87,7 +87,7 @@ var _ = Describe("Podman push", func() {
})
It("podman push to local registry with authorization", func() {
- SkipIfRootless() // FIXME: Creating content in certs.d we use directories in homedir
+ SkipIfRootless("FIXME: Creating content in certs.d we use directories in homedir")
if podmanTest.Host.Arch == "ppc64le" {
Skip("No registry image for ppc64le")
}
@@ -132,7 +132,7 @@ var _ = Describe("Podman push", func() {
Expect(session.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
session = podmanTest.PodmanNoCache([]string{"logs", "registry"})
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index cc2f7daf1..7eff8c6ed 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -228,11 +228,11 @@ var _ = Describe("Podman rm", func() {
session = podmanTest.Podman([]string{"rm", "bogus", "test1"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
session = podmanTest.Podman([]string{"rm", "test1", "bogus"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
})
It("podman rm --ignore bogus container and a running container", func() {
diff --git a/test/e2e/run_cgroup_parent_test.go b/test/e2e/run_cgroup_parent_test.go
index b10937953..5765d5ef6 100644
--- a/test/e2e/run_cgroup_parent_test.go
+++ b/test/e2e/run_cgroup_parent_test.go
@@ -18,7 +18,7 @@ var _ = Describe("Podman run with --cgroup-parent", func() {
)
BeforeEach(func() {
- SkipIfRootlessCgroupsV1() // cgroup parent is not supported in cgroups v1
+ SkipIfRootlessCgroupsV1("cgroup parent is not supported in cgroups v1")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -48,7 +48,7 @@ var _ = Describe("Podman run with --cgroup-parent", func() {
})
Specify("no --cgroup-parent", func() {
- SkipIfRootless() // FIXME This seems to be broken in rootless mode
+ SkipIfRootless("FIXME This seems to be broken in rootless mode")
cgroup := "/libpod_parent"
if !Containerized() && podmanTest.CgroupManager != "cgroupfs" {
cgroup = "/machine.slice"
diff --git a/test/e2e/run_cleanup_test.go b/test/e2e/run_cleanup_test.go
index 34b6ba4ff..5f6c9007a 100644
--- a/test/e2e/run_cleanup_test.go
+++ b/test/e2e/run_cleanup_test.go
@@ -34,7 +34,7 @@ var _ = Describe("Podman run exit", func() {
It("podman run -d mount cleanup test", func() {
SkipIfRemote("podman-remote does not support mount")
- SkipIfRootless() // FIXME podman mount requires podman unshare first
+ SkipIfRootless("FIXME podman mount requires podman unshare first")
result := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_cpu_test.go b/test/e2e/run_cpu_test.go
index 86cc9d1c5..d8b57c230 100644
--- a/test/e2e/run_cpu_test.go
+++ b/test/e2e/run_cpu_test.go
@@ -4,7 +4,6 @@ import (
"io/ioutil"
"os"
- "github.com/containers/podman/v2/pkg/cgroups"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -18,17 +17,14 @@ var _ = Describe("Podman run cpu", func() {
)
BeforeEach(func() {
- SkipIfRootlessCgroupsV1()
+ SkipIfRootlessCgroupsV1("Setting CPU not supported on cgroupv1 for rootless users")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
- if cgroupsv2 {
+ if CGROUPSV2 {
if err := ioutil.WriteFile("/sys/fs/cgroup/cgroup.subtree_control", []byte("+cpuset"), 0644); err != nil {
Skip("cpuset controller not available on the current kernel")
}
diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go
index 828da3494..1c2602631 100644
--- a/test/e2e/run_device_test.go
+++ b/test/e2e/run_device_test.go
@@ -72,7 +72,7 @@ var _ = Describe("Podman run device", func() {
})
It("podman run device host device and container device parameter are directories", func() {
- SkipIfRootless() // Can not create devices in /dev in rootless mode
+ SkipIfRootless("Cannot create devices in /dev in rootless mode")
Expect(os.MkdirAll("/dev/foodevdir", os.ModePerm)).To(BeNil())
defer os.RemoveAll("/dev/foodevdir")
diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go
index a3dc9bae5..b3913c1e6 100644
--- a/test/e2e/run_memory_test.go
+++ b/test/e2e/run_memory_test.go
@@ -3,7 +3,6 @@ package integration
import (
"os"
- "github.com/containers/podman/v2/pkg/cgroups"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -17,9 +16,8 @@ var _ = Describe("Podman run memory", func() {
)
BeforeEach(func() {
- SkipIfRootlessCgroupsV1()
+ SkipIfRootlessCgroupsV1("Setting Memory not supported on cgroupv1 for rootless users")
- SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -37,12 +35,9 @@ var _ = Describe("Podman run memory", func() {
})
It("podman run memory test", func() {
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
var session *PodmanSessionIntegration
- if cgroupsv2 {
+ if CGROUPSV2 {
session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.max"})
} else {
session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"})
@@ -57,28 +52,21 @@ var _ = Describe("Podman run memory", func() {
Skip("Unable to perform test on Ubuntu distributions due to memory management")
}
- cgroupsv2, err := cgroups.IsCgroup2UnifiedMode()
- Expect(err).To(BeNil())
-
var session *PodmanSessionIntegration
- if cgroupsv2 {
- session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.high"})
+ if CGROUPSV2 {
+ session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"})
} else {
session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"})
}
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
- if cgroupsv2 {
- Expect(session.OutputToString()).To(Equal("max"))
- } else {
- Expect(session.OutputToString()).To(Equal("41943040"))
- }
+ Expect(session.OutputToString()).To(Equal("41943040"))
})
It("podman run memory-swappiness test", func() {
- SkipIfCgroupV2()
+ SkipIfCgroupV2("memory-swappiness not supported on cgroupV2")
session := podmanTest.Podman([]string{"run", "--memory-swappiness=15", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -86,8 +74,18 @@ var _ = Describe("Podman run memory", func() {
})
It("podman run kernel-memory test", func() {
- SkipIfCgroupV2()
- session := podmanTest.Podman([]string{"run", "--kernel-memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"})
+ if podmanTest.Host.Distribution == "ubuntu" {
+ Skip("Unable to perform test on Ubuntu distributions due to memory management")
+ }
+
+ var session *PodmanSessionIntegration
+
+ if CGROUPSV2 {
+ session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/memory.low"})
+ } else {
+ session = podmanTest.Podman([]string{"run", "--memory-reservation=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"})
+ }
+
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("41943040"))
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index a67324b2b..044e56e6c 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -55,7 +55,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network expose port 222", func() {
- SkipIfRootless() // iptables is not supported for rootless users
+ SkipIfRootless("iptables is not supported for rootless users")
session := podmanTest.Podman([]string{"run", "-dt", "--expose", "222-223", "-P", ALPINE, "/bin/sh"})
session.Wait(30)
Expect(session.ExitCode()).To(Equal(0))
@@ -252,7 +252,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network expose host port 80 to container port 8000", func() {
- SkipIfRootless() // iptables is not supported for rootless users
+ SkipIfRootless("iptables is not supported for rootless users")
session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"})
session.Wait(30)
Expect(session.ExitCode()).To(Equal(0))
@@ -367,7 +367,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network expose duplicate host port results in error", func() {
- SkipIfRootless() // FIXME we should be able to run this test in rootless mode with different ports
+ SkipIfRootless("FIXME we should be able to run this test in rootless mode with different ports")
session := podmanTest.Podman([]string{"run", "--name", "test", "-dt", "-p", "80", ALPINE, "/bin/sh"})
session.WaitWithDefaultTimeout()
@@ -478,9 +478,9 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network in user created network namespace", func() {
- SkipIfRootless() // ip netns is not supported for rootless users
+ SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
- Skip("Can not be run within a container.")
+ Skip("Cannot be run within a container.")
}
addXXX := SystemExec("ip", []string{"netns", "add", "xxx"})
Expect(addXXX.ExitCode()).To(Equal(0))
@@ -495,9 +495,9 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run n user created network namespace with resolv.conf", func() {
- SkipIfRootless() // ip netns is not supported for rootless users
+ SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
- Skip("Can not be run within a container.")
+ Skip("Cannot be run within a container.")
}
addXXX2 := SystemExec("ip", []string{"netns", "add", "xxx2"})
Expect(addXXX2.ExitCode()).To(Equal(0))
@@ -527,7 +527,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run in custom CNI network with --static-ip", func() {
- SkipIfRootless() //Rootless mode does not support --ip
+ SkipIfRootless("Rootless mode does not support --ip")
netName := "podmantestnetwork"
ipAddr := "10.25.30.128"
create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName})
@@ -542,7 +542,7 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run with new:pod and static-ip", func() {
- SkipIfRootless() // Rootless does not support --ip
+ SkipIfRootless("Rootless does not support --ip")
netName := "podmantestnetwork2"
ipAddr := "10.25.40.128"
podname := "testpod"
diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go
index a20088776..ab11128ba 100644
--- a/test/e2e/run_privileged_test.go
+++ b/test/e2e/run_privileged_test.go
@@ -106,7 +106,7 @@ var _ = Describe("Podman privileged container tests", func() {
})
It("podman privileged should inherit host devices", func() {
- SkipIfRootless() // FIXME: This seems to be broken for rootless mode, /dev/ is close to the same
+ SkipIfRootless("FIXME: This seems to be broken for rootless mode, /dev/ is close to the same")
session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "-l", "/dev"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/run_security_labels.go b/test/e2e/run_security_labels.go
index 7c8597866..2a0b0467d 100644
--- a/test/e2e/run_security_labels.go
+++ b/test/e2e/run_security_labels.go
@@ -130,7 +130,7 @@ var _ = Describe("Podman generate kube", func() {
SkipIfRemote("runlabel not supported on podman-remote")
PodmanDockerfile := `
FROM alpine:latest
-LABEL io.containers.capabilities=chown,mknod`
+LABEL io.containers.capabilities=chown,kill`
image := "podman-caps:podman"
podmanTest.BuildImage(PodmanDockerfile, image, "false")
@@ -145,7 +145,7 @@ LABEL io.containers.capabilities=chown,mknod`
ctr := inspect.InspectContainerToJSON()
caps := strings.Join(ctr[0].EffectiveCaps, ",")
- Expect(caps).To(Equal("CAP_CHOWN,CAP_MKNOD"))
+ Expect(caps).To(Equal("CAP_CHOWN,CAP_KILL"))
})
})
diff --git a/test/e2e/run_staticip_test.go b/test/e2e/run_staticip_test.go
index 959c823b5..8383b1812 100644
--- a/test/e2e/run_staticip_test.go
+++ b/test/e2e/run_staticip_test.go
@@ -19,7 +19,7 @@ var _ = Describe("Podman run with --ip flag", func() {
)
BeforeEach(func() {
- SkipIfRootless() //rootless does not support --ip
+ SkipIfRootless("rootless does not support --ip")
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 5617f50b7..292df529c 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -261,6 +261,8 @@ var _ = Describe("Podman run", func() {
})
It("podman run user capabilities test", func() {
+ // We need to ignore the containers.conf on the test distribution for this test
+ os.Setenv("CONTAINERS_CONF", "/dev/null")
session := podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapBnd", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -293,6 +295,8 @@ var _ = Describe("Podman run", func() {
})
It("podman run user capabilities test with image", func() {
+ // We need to ignore the containers.conf on the test distribution for this test
+ os.Setenv("CONTAINERS_CONF", "/dev/null")
SkipIfRemote("FIXME This should work on podman-remote")
dockerfile := `FROM busybox
USER bin`
@@ -309,7 +313,7 @@ USER bin`
})
It("podman run limits test", func() {
- SkipIfRootlessCgroupsV1()
+ SkipIfRootlessCgroupsV1("Setting limits not supported on cgroupv1 for rootless users")
if !isRootless() {
session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "rtprio=99", "--cap-add=sys_nice", fedoraMinimal, "cat", "/proc/self/sched"})
@@ -368,7 +372,7 @@ USER bin`
})
It("podman run sysctl test", func() {
- SkipIfRootless() // Network sysclts are not avalable root rootless
+ SkipIfRootless("Network sysctls are not avalable root rootless")
session := podmanTest.Podman([]string{"run", "--rm", "--sysctl", "net.core.somaxconn=65535", ALPINE, "sysctl", "net.core.somaxconn"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -381,8 +385,8 @@ USER bin`
})
It("podman run blkio-weight test", func() {
- SkipIfRootless() // FIXME: This is blowing up because of no /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control file
- // SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME: This is blowing up because of no /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control file")
+ SkipIfRootlessCgroupsV1("Setting blkio-weight not supported on cgroupv1 for rootless users")
if !CGROUPSV2 {
if _, err := os.Stat("/sys/fs/cgroup/blkio/blkio.weight"); os.IsNotExist(err) {
Skip("Kernel does not support blkio.weight")
@@ -404,8 +408,9 @@ USER bin`
})
It("podman run device-read-bps test", func() {
- SkipIfRootless() // FIXME: Missing /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control
- SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME: Missing /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control")
+ SkipIfRootlessCgroupsV1("Setting device-read-bps not supported on cgroupv1 for rootless users")
+
var session *PodmanSessionIntegration
if CGROUPSV2 {
@@ -422,8 +427,9 @@ USER bin`
})
It("podman run device-write-bps test", func() {
- SkipIfRootless() // FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist
- SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist")
+ SkipIfRootlessCgroupsV1("Setting device-write-bps not supported on cgroupv1 for rootless users")
+
var session *PodmanSessionIntegration
if CGROUPSV2 {
@@ -439,8 +445,8 @@ USER bin`
})
It("podman run device-read-iops test", func() {
- SkipIfRootless() // FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist
- SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist")
+ SkipIfRootlessCgroupsV1("Setting device-read-iops not supported on cgroupv1 for rootless users")
var session *PodmanSessionIntegration
if CGROUPSV2 {
@@ -457,8 +463,8 @@ USER bin`
})
It("podman run device-write-iops test", func() {
- SkipIfRootless() // FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist
- SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME /sys/fs/cgroup/user.slice/user-14467.slice/user@14467.service/cgroup.subtree_control does not exist")
+ SkipIfRootlessCgroupsV1("Setting device-write-iops not supported on cgroupv1 for rootless users")
var session *PodmanSessionIntegration
if CGROUPSV2 {
@@ -575,7 +581,7 @@ USER bin`
})
It("podman run with FIPS mode secrets", func() {
- SkipIfRootless() // rootless can not manipulate system-fips file
+ SkipIfRootless("rootless can not manipulate system-fips file")
fipsFile := "/etc/system-fips"
err = ioutil.WriteFile(fipsFile, []byte{}, 0755)
Expect(err).To(BeNil())
@@ -894,7 +900,7 @@ USER mail`
})
It("podman run --mount type=bind,bind-nonrecursive", func() {
- SkipIfRootless() // rootless users are not allowed to mount bind-nonrecursive (Could this be a Kernel bug?
+ SkipIfRootless("FIXME: rootless users are not allowed to mount bind-nonrecursive (Could this be a Kernel bug?")
session := podmanTest.Podman([]string{"run", "--mount", "type=bind,bind-nonrecursive,slave,src=/,target=/host", fedoraMinimal, "findmnt", "-nR", "/host"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@@ -1054,8 +1060,8 @@ USER mail`
})
It("podman run with cgroups=disabled runs without cgroups", func() {
- SkipIfRootless() // FIXME: I believe this should work but need to fix this test
- SkipIfRootlessCgroupsV1()
+ SkipIfRootless("FIXME: I believe this should work but need to fix this test")
+ SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users")
// Only works on crun
if !strings.Contains(podmanTest.OCIRuntime, "crun") {
Skip("Test only works on crun")
@@ -1087,7 +1093,7 @@ USER mail`
})
It("podman run with cgroups=enabled makes cgroups", func() {
- SkipIfRootlessCgroupsV1()
+ SkipIfRootlessCgroupsV1("Enable cgroups not supported on cgroupv1 for rootless users")
// Only works on crun
if !strings.Contains(podmanTest.OCIRuntime, "crun") {
Skip("Test only works on crun")
@@ -1130,9 +1136,9 @@ USER mail`
})
It("podman run --device-cgroup-rule", func() {
- SkipIfRootless() // rootless users are not allowed to mknod
+ SkipIfRootless("rootless users are not allowed to mknod")
deviceCgroupRule := "c 42:* rwm"
- session := podmanTest.Podman([]string{"run", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"})
+ session := podmanTest.Podman([]string{"run", "--cap-add", "mknod", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "test", "mknod", "newDev", "c", "42", "1"})
@@ -1208,7 +1214,7 @@ USER mail`
})
It("podman run verify pids-limit", func() {
- SkipIfCgroupV1()
+ SkipIfCgroupV1("pids-limit not supported on cgroup V1")
limit := "4321"
session := podmanTest.Podman([]string{"run", "--pids-limit", limit, "--rm", ALPINE, "cat", "/sys/fs/cgroup/pids.max"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index fc9245e62..92d3418e3 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -227,7 +227,7 @@ var _ = Describe("Podman run with volumes", func() {
})
It("podman run with tmpfs named volume mounts and unmounts", func() {
- SkipIfRootless() // FIXME: rootless podman mount requires you to be in a user namespace
+ SkipIfRootless("FIXME: rootless podman mount requires you to be in a user namespace")
SkipIfRemote("podman-remote does not support --volumes this test could be simplified to be tested on Remote.")
volName := "testvol"
mkVolume := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=nodev", "testvol"})
diff --git a/test/e2e/search_test.go b/test/e2e/search_test.go
index a3d56ad89..19365909d 100644
--- a/test/e2e/search_test.go
+++ b/test/e2e/search_test.go
@@ -186,7 +186,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(fakereg.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
search := podmanTest.Podman([]string{"search",
@@ -213,7 +213,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registry.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry3", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
podmanTest.RestoreArtifact(ALPINE)
@@ -250,7 +250,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registry.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry4", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
podmanTest.RestoreArtifact(ALPINE)
@@ -290,7 +290,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registry.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry5", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
podmanTest.RestoreArtifact(ALPINE)
@@ -329,7 +329,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registry.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry6", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
podmanTest.RestoreArtifact(ALPINE)
@@ -371,7 +371,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registryLocal.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry7", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
registryLocal = podmanTest.Podman([]string{"run", "-d", "-p", "6000:5000", "--name", "registry8", registry})
@@ -379,7 +379,7 @@ registries = ['{{.Host}}:{{.Port}}']`
Expect(registryLocal.ExitCode()).To(Equal(0))
if !WaitContainerReady(podmanTest, "registry8", "listening on", 20, 1) {
- Skip("Can not start docker registry.")
+ Skip("Cannot start docker registry.")
}
podmanTest.RestoreArtifact(ALPINE)
diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go
index 7ab435007..c8f5efa9d 100644
--- a/test/e2e/stats_test.go
+++ b/test/e2e/stats_test.go
@@ -21,9 +21,7 @@ var _ = Describe("Podman stats", func() {
)
BeforeEach(func() {
- if os.Geteuid() != 0 {
- SkipIfCgroupV1()
- }
+ SkipIfRootlessCgroupsV1("stats not supported on cgroupv1 for rootless users")
var err error
tempdir, err = CreateTempDirInTempDir()
if err != nil {
diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go
index 8ef1e3ac7..9e717a0eb 100644
--- a/test/e2e/systemd_test.go
+++ b/test/e2e/systemd_test.go
@@ -47,10 +47,8 @@ WantedBy=multi-user.target
})
It("podman start container by systemd", func() {
- SkipIfRootless() // rootless can not write to /etc
- if os.Getenv("SKIP_USERNS") != "" {
- Skip("Skip userns tests.")
- }
+ SkipIfRootless("rootless can not write to /etc")
+ SkipIfContainerized("test does not have systemd as pid 1")
sys_file := ioutil.WriteFile("/etc/systemd/system/redis.service", []byte(systemd_unit_file), 0644)
Expect(sys_file).To(BeNil())
diff --git a/test/e2e/tree_test.go b/test/e2e/tree_test.go
index c6eb0a6eb..eeb00440c 100644
--- a/test/e2e/tree_test.go
+++ b/test/e2e/tree_test.go
@@ -34,9 +34,7 @@ var _ = Describe("Podman image tree", func() {
})
It("podman image tree", func() {
- if podmanTest.RemoteTest {
- Skip("Does not work on remote client")
- }
+ SkipIfRemote("Does not work on remote client")
dockerfile := `FROM docker.io/library/busybox:latest
RUN mkdir hello
RUN touch test.txt
diff --git a/test/e2e/untag_test.go b/test/e2e/untag_test.go
index 7766ce634..91a933090 100644
--- a/test/e2e/untag_test.go
+++ b/test/e2e/untag_test.go
@@ -33,7 +33,6 @@ var _ = Describe("Podman untag", func() {
})
It("podman untag all", func() {
- SkipIfRemote("FIXME This should work on podman-remote")
setup := podmanTest.PodmanNoCache([]string{"pull", ALPINE})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go
index 377b721d0..4a2c2d324 100644
--- a/test/e2e/volume_ls_test.go
+++ b/test/e2e/volume_ls_test.go
@@ -56,7 +56,7 @@ var _ = Describe("Podman volume ls", func() {
})
It("podman ls volume with Go template", func() {
- Skip(v2fail)
+ Skip("FIXME: table still not supported in podman volume command")
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go
index a072bc824..cdced1f13 100644
--- a/test/e2e/volume_rm_test.go
+++ b/test/e2e/volume_rm_test.go
@@ -55,7 +55,7 @@ var _ = Describe("Podman volume rm", func() {
session = podmanTest.Podman([]string{"volume", "rm", "myvol"})
session.WaitWithDefaultTimeout()
- Expect(session).To(ExitWithError())
+ Expect(session.ExitCode()).To(Equal(2))
Expect(session.ErrorToString()).To(ContainSubstring(cid))
session = podmanTest.Podman([]string{"volume", "rm", "-f", "myvol"})
@@ -70,6 +70,12 @@ var _ = Describe("Podman volume rm", func() {
podmanTest.Cleanup()
})
+ It("podman volume remove bogus", func() {
+ session := podmanTest.Podman([]string{"volume", "rm", "bogus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
+
It("podman rm with --all flag", func() {
session := podmanTest.Podman([]string{"volume", "create", "myvol"})
session.WaitWithDefaultTimeout()
diff --git a/test/registries.conf b/test/registries.conf
index bb7072d45..f27a282d6 100644
--- a/test/registries.conf
+++ b/test/registries.conf
@@ -1,3 +1,4 @@
+# Note that changing the order here may break tests.
[registries.search]
registries = ['docker.io', 'quay.io', 'registry.fedoraproject.org']
diff --git a/test/system/020-tag.bats b/test/system/020-tag.bats
index 7593ad68f..1f5eede39 100644
--- a/test/system/020-tag.bats
+++ b/test/system/020-tag.bats
@@ -32,4 +32,23 @@ function _tag_and_check() {
is "$output" "Error: \"registry.com/foo:bar\": no such tag"
}
+@test "podman untag all" {
+ # First get the image ID
+ run_podman inspect --format '{{.ID}}' $IMAGE
+ iid=$output
+
+ # Add a couple of tags
+ run_podman tag $IMAGE registry.com/1:latest registry.com/2:latest registry.com/3:latest
+
+ # Untag with arguments to for all tags to be removed
+ run_podman untag $iid
+
+ # Now make sure all tags are removed
+ run_podman image inspect $iid --format "{{.RepoTags}}"
+ is "$output" "\[\]" "untag by ID leaves empty set of tags"
+
+ # Restore image
+ run_podman tag $iid $IMAGE
+}
+
# vim: filetype=sh
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index b3599cc17..766948ecc 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -153,8 +153,23 @@ echo $rand | 0 | $rand
run_podman run --pull=always $NONLOCAL_IMAGE true
is "$output" "Trying to pull .*" "--pull=always [with image PRESENT]: re-fetches"
+ # Very weird corner case fixed by #7770: 'podman run foo' will run 'myfoo'
+ # if it exists, because the string 'foo' appears in 'myfoo'. This test
+ # covers that, as well as making sure that our testimage (which is always
+ # tagged :YYYYMMDD, never :latest) doesn't match either.
+ run_podman tag $IMAGE my${PODMAN_TEST_IMAGE_NAME}:latest
+ run_podman 125 run --pull=never $PODMAN_TEST_IMAGE_NAME true
+ is "$output" "Error: unable to find a name and tag match for $PODMAN_TEST_IMAGE_NAME in repotags: no such image" \
+ "podman run --pull=never with shortname (and implicit :latest)"
+
+ # ...but if we add a :latest tag (without 'my'), it should now work
+ run_podman tag $IMAGE ${PODMAN_TEST_IMAGE_NAME}:latest
+ run_podman run --pull=never ${PODMAN_TEST_IMAGE_NAME} cat /home/podman/testimage-id
+ is "$output" "$PODMAN_TEST_IMAGE_TAG" \
+ "podman run --pull=never, with shortname, succeeds if img is present"
+
run_podman rm -a
- run_podman rmi $NONLOCAL_IMAGE
+ run_podman rmi $NONLOCAL_IMAGE {my,}${PODMAN_TEST_IMAGE_NAME}:latest
}
# 'run --rmi' deletes the image in the end unless it's used by another container
@@ -304,7 +319,7 @@ echo $rand | 0 | $rand
# #6991 : /etc/passwd is modifiable
@test "podman run : --userns=keep-id: passwd file is modifiable" {
- run_podman run -d --userns=keep-id $IMAGE sh -c 'while ! test -e /stop; do sleep 0.1; done'
+ run_podman run -d --userns=keep-id --cap-add=dac_override $IMAGE sh -c 'while ! test -e /tmp/stop; do sleep 0.1; done'
cid="$output"
# Assign a UID that is (a) not in our image /etc/passwd and (b) not
@@ -325,7 +340,7 @@ echo $rand | 0 | $rand
is "$output" "newuser3:x:$uid:999:$gecos:/home/newuser3:/bin/sh" \
"newuser3 added to /etc/passwd in container"
- run_podman exec $cid touch /stop
+ run_podman exec $cid touch /tmp/stop
run_podman wait $cid
}
@@ -393,4 +408,28 @@ json-file | f
run_podman rm myctr
}
+@test "podman run --tz" {
+ # This file will always have a constant reference timestamp
+ local testfile=/home/podman/testimage-id
+
+ run_podman run --rm $IMAGE date -r $testfile
+ is "$output" "Sun Sep 13 12:26:40 UTC 2020" "podman run with no TZ"
+
+ run_podman run --rm --tz=MST7MDT $IMAGE date -r $testfile
+ is "$output" "Sun Sep 13 06:26:40 MDT 2020" "podman run with --tz=MST7MDT"
+
+ # --tz=local pays attention to /etc/localtime, not $TZ. We set TZ anyway,
+ # to make sure podman ignores it; and, because this test is locale-
+ # dependent, we pick an obscure zone (+1245) that is unlikely to
+ # collide with any of our testing environments.
+ #
+ # To get a reference timestamp we run 'date' locally; note the explicit
+ # strftime() format. We can't use --iso=seconds because GNU date adds
+ # a colon to the TZ offset (eg -07:00) whereas alpine does not (-0700).
+ run date --date=@1600000000 +%Y-%m-%dT%H:%M:%S%z
+ expect="$output"
+ TZ=Pacific/Chatham run_podman run --rm --tz=local $IMAGE date -Iseconds -r $testfile
+ is "$output" "$expect" "podman run with --tz=local, matches host"
+}
+
# vim: filetype=sh
diff --git a/test/system/075-exec.bats b/test/system/075-exec.bats
index e9db8c27e..edd7dedc4 100644
--- a/test/system/075-exec.bats
+++ b/test/system/075-exec.bats
@@ -92,14 +92,14 @@ load helpers
# #6829 : add username to /etc/passwd inside container if --userns=keep-id
@test "podman exec - with keep-id" {
run_podman run -d --userns=keep-id $IMAGE sh -c \
- "echo READY;while [ ! -f /stop ]; do sleep 1; done"
+ "echo READY;while [ ! -f /tmp/stop ]; do sleep 1; done"
cid="$output"
wait_for_ready $cid
run_podman exec $cid id -un
is "$output" "$(id -un)" "container is running as current user"
- run_podman exec --user=$(id -un) $cid touch /stop
+ run_podman exec --user=$(id -un) $cid touch /tmp/stop
run_podman wait $cid
run_podman rm $cid
}
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index d2454fbf4..150626ded 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -99,7 +99,7 @@ load helpers
"Trying to create an already-existing network"
run_podman network rm $mynetname
- run_podman 125 network rm $mynetname
+ run_podman 1 network rm $mynetname
# rootless CNI leaves behind an image pulled by SHA, hence with no tag.
# Remove it if present; we can only remove it by ID.
diff --git a/test/system/build-testimage b/test/system/build-testimage
index ef14d3afd..53ade57f0 100755
--- a/test/system/build-testimage
+++ b/test/system/build-testimage
@@ -35,6 +35,12 @@ cd $tmpdir
# 'image mount' test will confirm that this file exists and has our YMD tag
echo $YMD >testimage-id
+# ...but set the timestamp on the file itself to a constant well-known
+# value, for use by the 'run --tz' test. Date value chosen for nerdiness
+# and because it's in the past. (Much as I'd love FFFFFFFF, we can't
+# use any future date because of unpredictable leap second adjustments).
+touch --date=@1600000000 testimage-id
+
# 'pod' test will use this for --infra-command
cat >pause <<EOF
#!/bin/sh
@@ -49,6 +55,7 @@ EOF
chmod 755 pause
# alpine because it's small and light and reliable
+# - check for updates @ https://hub.docker.com/_/alpine
# busybox-extras provides httpd needed in 500-networking.bats
cat >Containerfile <<EOF
FROM docker.io/library/alpine:3.12.0
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index eb3e4c7ec..998db5283 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -7,7 +7,7 @@ PODMAN=${PODMAN:-podman}
PODMAN_TEST_IMAGE_REGISTRY=${PODMAN_TEST_IMAGE_REGISTRY:-"quay.io"}
PODMAN_TEST_IMAGE_USER=${PODMAN_TEST_IMAGE_USER:-"libpod"}
PODMAN_TEST_IMAGE_NAME=${PODMAN_TEST_IMAGE_NAME:-"testimage"}
-PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"20200917"}
+PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"20200929"}
PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG"
# Because who wants to spell that out each time?
diff --git a/transfer.md b/transfer.md
index 49ff687e6..8ead43f63 100644
--- a/transfer.md
+++ b/transfer.md
@@ -89,6 +89,14 @@ There are other equivalents for these tools
**** Use mount to take advantage of the entire linux tool chain rather then just cp. Read [`here`](./docs/podman-cp.1.md) for more information.
+## Behavioural differences and pitfalls
+
+These commands behave differently from the commands in Docker:
+
+| Command | Description |
+| :--- | :--- |
+| `podman volume create` | While `docker volume create` is idempotent, `podman volume create` raises an error if the volume does already exist. See this [docker issue](https://github.com/moby/moby/issues/16068) for more information.|
+
## Missing commands in podman
Those Docker commands currently do not have equivalents in `podman`: