summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md34
-rw-r--r--cmd/podman/common/create.go9
-rw-r--r--cmd/podman/common/create_opts.go1
-rw-r--r--cmd/podman/common/specgen.go1
-rw-r--r--cmd/podman/images/pull.go1
-rw-r--r--cmd/podman/images/push.go1
-rw-r--r--completions/bash/podman5
-rw-r--r--docs/source/markdown/podman-create.1.md23
-rw-r--r--docs/source/markdown/podman-pull.1.md6
-rw-r--r--docs/source/markdown/podman-push.1.md6
-rw-r--r--docs/source/markdown/podman-run.1.md13
-rw-r--r--go.mod2
-rw-r--r--go.sum2
-rw-r--r--libpod/container.go6
-rw-r--r--libpod/container_exec.go4
-rw-r--r--libpod/container_inspect.go8
-rw-r--r--libpod/container_internal_linux.go8
-rw-r--r--libpod/define/config.go2
-rw-r--r--libpod/define/container_inspect.go2
-rw-r--r--libpod/events/journal_linux.go7
-rw-r--r--libpod/events/logfile.go8
-rw-r--r--libpod/healthcheck.go5
-rw-r--r--libpod/options.go14
-rw-r--r--pkg/api/handlers/compat/events.go154
-rw-r--r--pkg/api/server/register_generate.go4
-rw-r--r--pkg/bindings/test/system_test.go29
-rw-r--r--pkg/domain/infra/abi/play.go11
-rw-r--r--pkg/specgen/generate/container_create.go3
-rw-r--r--pkg/specgen/specgen.go2
-rw-r--r--test/e2e/config/containers.conf2
-rw-r--r--test/e2e/containers_conf_test.go11
-rw-r--r--test/e2e/create_test.go43
-rw-r--r--test/e2e/events_test.go4
-rw-r--r--test/e2e/play_kube_test.go82
-rw-r--r--test/e2e/run_test.go31
-rw-r--r--test/system/015-help.bats21
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/generate.go77
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go14
-rw-r--r--vendor/modules.txt2
39 files changed, 527 insertions, 131 deletions
diff --git a/README.md b/README.md
index 82e2d3af9..21b30fdbd 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
![PODMAN logo](logo/podman-logo-source.svg)
-# Library and tool for running OCI-based containers in Pods
+# Podman: A tool for managing OCI containers and pods
-Libpod provides a library for applications looking to use the Container Pod concept,
-popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes.
+Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers.
+Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes.
* [Latest Version: 2.0.2](https://github.com/containers/libpod/releases/latest)
* Latest Remote client for Windows
@@ -15,26 +15,24 @@ popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`
## Overview and scope
-At a high level, the scope of libpod and Podman is the following:
+At a high level, the scope of Podman and libpod is the following:
-* Support multiple image formats including the OCI and Docker image formats.
-* Support for multiple means to download images including trust & image verification.
-* Container image management (managing image layers, overlay filesystems, etc).
-* Full management of container lifecycle.
-* Support for pods to manage groups of containers together.
+* Support for multiple container image formats, including OCI and Docker images.
+* Full management of those images, including pulling from various sources (including trust and verification), creating (built via Containerfile or Dockerfile or committed from a container), and pushing to registries and other storage backends.
+* Full management of container lifecycle, including creation (both from an image and from an exploded root filesystem), running, checkpointing and restoring (via CRIU), and removal.
+* Support for pods, groups of containers that share resources and are managed together.
* Resource isolation of containers and pods.
-* Support for a Docker-compatible CLI interface through Podman.
+* Support for a Docker-compatible CLI interface.
* Support for a REST API providing both a Docker-compatible interface and an improved interface exposing advanced Podman functionality.
-* Integration with CRI-O to share containers and backend code.
+* In the future, integration with [CRI-O](https://github.com/cri-o/cri-o) to share containers and backend code.
Podman presently only supports running containers on Linux. However, we are building a remote client which can run on Windows and OS X and manage Podman containers on a Linux system via the REST API using SSH tunneling.
## Roadmap
-1. Complete the Podman REST API and Podman v2, which will be able to connect to remote Podman instances via this API
-1. Integrate libpod into CRI-O to replace its existing container management backend
-1. Further work on the podman pod command
-1. Further improvements on rootless containers
+1. Further improvements to the REST API, with a focus on bugfixes and implementing missing functionality
+1. Integrate libpod into [CRI-O](https://github.com/cri-o/cri-o) to replace its existing container management backend
+1. Improvements on rootless containers, with a focus on improving the user experience and exposing presently-unavailable features when possible
## Communications
@@ -67,10 +65,10 @@ A little configuration by an administrator is required before rootless Podman ca
## Out of scope
-* Specializing in signing and pushing images to various storage backends.
+* Specialized signing and pushing of images to various storage backends.
See [Skopeo](https://github.com/containers/skopeo/) for those tasks.
-* Container runtimes daemons for working with the Kubernetes CRI interface.
- [CRI-O](https://github.com/cri-o/cri-o) specializes in that.
+* Support for the Kubernetes CRI interface for container management.
+ The [CRI-O](https://github.com/cri-o/cri-o) daemon specializes in that.
* Supporting `docker-compose`. We believe that Kubernetes is the defacto
standard for composing Pods and for orchestrating containers, making
Kubernetes YAML a defacto standard file format. Hence, Podman allows the
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index a26bbf718..46f78cdba 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -155,6 +155,10 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"device-write-iops", []string{},
"Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
)
+ createFlags.Bool(
+ "disable-content-trust", false,
+ "This is a Docker specific option and is a NOOP",
+ )
createFlags.String("entrypoint", "",
"Overwrite the default ENTRYPOINT of the image",
)
@@ -459,6 +463,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
"tz", containerConfig.TZ(),
"Set timezone in container",
)
+ createFlags.StringVar(
+ &cf.Umask,
+ "umask", containerConfig.Umask(),
+ "Set umask in container",
+ )
createFlags.StringSliceVar(
&cf.UIDMap,
"uidmap", []string{},
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index a544846aa..2bea8b0b4 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -93,6 +93,7 @@ type ContainerCLIOpts struct {
TmpFS []string
TTY bool
Timezone string
+ Umask string
UIDMap []string
Ulimit []string
User string
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 416c6f6ec..731085731 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -613,6 +613,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Remove = c.Rm
s.StopTimeout = &c.StopTimeout
s.Timezone = c.Timezone
+ s.Umask = c.Umask
return nil
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index 83bb186df..c10a351d8 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -82,6 +82,7 @@ func pullFlags(flags *pflag.FlagSet) {
flags.StringVar(&pullOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images")
flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images")
+ flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP")
flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index 4eeed13d4..480b5e0f0 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -79,6 +79,7 @@ func pushFlags(flags *pflag.FlagSet) {
flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&pushOptions.CredentialsCLI, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pushOptions.DigestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
+ flags.Bool("disable-content-trust", false, "This is a Docker specific option and is a NOOP")
flags.StringVarP(&pushOptions.Format, "format", "f", "", "Manifest type (oci, v2s1, or v2s2) to use when pushing an image using the 'dir' transport (default is manifest type of source)")
flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images")
flags.BoolVar(&pushOptions.RemoveSignatures, "remove-signatures", false, "Discard any pre-existing signatures in the image")
diff --git a/completions/bash/podman b/completions/bash/podman
index 458090ac4..eb727ef63 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2119,12 +2119,13 @@ _podman_container_run() {
--shm-size
--stop-signal
--stop-timeout
- --tmpfs
- --tz
--subgidname
--subuidname
--sysctl
--systemd
+ --tmpfs
+ --tz
+ --umask
--uidmap
--ulimit
--user -u
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index a422dd184..b4456225e 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -234,6 +234,12 @@ Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sd
Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--dns**=*dns*
Set custom DNS servers. Invalid if using **--dns** and **--network** that is set to 'none' or 'container:<name|id>'.
@@ -833,6 +839,10 @@ standard input.
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
+**--umask**=*umask*
+
+Set the umask inside the container. Defaults to `0022`.
+
**--uidmap**=*container_uid:host_uid:amount*
UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags.
@@ -1120,14 +1130,13 @@ required for VPN, without it containers need to be run with the --network=host f
Environment variables within containers can be set using multiple different options: This section describes the precedence.
-Precedence Order:
- **--env-host** : Host environment of the process executing Podman is added.
-
- Container image : Any environment variables specified in the container image.
-
- **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry.
+Precedence order (later entries override earlier entries):
- **--env** : Any environment variables specified will override previous settings.
+- **--env-host** : Host environment of the process executing Podman is added.
+- **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details.
+- Container image : Any environment variables specified in the container image.
+- **--env-file** : Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry.
+- **--env** : Any environment variables specified will override previous settings.
Create containers and set the environment ending with a __*__ and a *****
diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md
index 5d941219a..201b10aa6 100644
--- a/docs/source/markdown/podman-pull.1.md
+++ b/docs/source/markdown/podman-pull.1.md
@@ -73,6 +73,12 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--override-os**=*OS*
Use OS instead of the running OS for choosing images
diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md
index f029c8db1..fffd76801 100644
--- a/docs/source/markdown/podman-push.1.md
+++ b/docs/source/markdown/podman-push.1.md
@@ -71,6 +71,12 @@ Note: This flag can only be set when using the **dir** transport
After copying the image, write the digest of the resulting image to the file. (Not available for remote commands)
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--format**, **-f**=*format*
Manifest Type (oci, v2s1, or v2s2) to use when pushing an image to a directory using the 'dir:' transport (default is manifest type of source)
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index a7fd5a7eb..303d025b0 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -247,6 +247,12 @@ Limit write rate (in bytes per second) to a device (e.g. **--device-write-bps=/d
Limit write rate (in IO operations per second) to a device (e.g. **--device-write-iops=/dev/sda:1000**).
+**--disable-content-trust**
+
+This is a Docker specific option to disable image verification to a Docker
+registry and is not supported by Podman. This flag is a NOOP and provided
+solely for scripting compatibility.
+
**--dns**=*ipaddr*
Set custom DNS servers. Invalid if using **--dns** with **--network** that is set to **none** or **container:**_id_.
@@ -874,6 +880,10 @@ standard input.
Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones.
+**--umask**=*umask*
+
+Set the umask inside the container. Defaults to `0022`.
+
**--uidmap**=*container_uid*:*host_uid*:*amount*
Run the container in a new user namespace using the supplied mapping. This option conflicts
@@ -1399,9 +1409,10 @@ required for VPN, without it containers need to be run with the **--network=host
## ENVIRONMENT
Environment variables within containers can be set using multiple different options,
-in the following order of precedence:
+in the following order of precedence (later entries override earlier entries):
- **--env-host**: Host environment of the process executing Podman is added.
+- **--http-proxy**: By default, several environment variables will be passed in from the host, such as **http_proxy** and **no_proxy**. See **--http-proxy** for details.
- Container image: Any environment variables specified in the container image.
- **--env-file**: Any environment variables specified via env-files. If multiple files specified, then they override each other in order of entry.
- **--env**: Any environment variables specified will override previous settings.
diff --git a/go.mod b/go.mod
index bf9718dea..bd5a0cfe3 100644
--- a/go.mod
+++ b/go.mod
@@ -41,7 +41,7 @@ require (
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/runc v1.0.0-rc91.0.20200708210054-ce54a9d4d79b
github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2
- github.com/opencontainers/runtime-tools v0.9.0
+ github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa
github.com/opencontainers/selinux v1.6.0
github.com/opentracing/opentracing-go v1.2.0
github.com/pkg/errors v0.9.1
diff --git a/go.sum b/go.sum
index 7f95292ac..0cafe0822 100644
--- a/go.sum
+++ b/go.sum
@@ -342,6 +342,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.m
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa h1:iyj+fFHVBn0xOalz9UChYzSU1K0HJ+d75b4YqShBRhI=
+github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
diff --git a/libpod/container.go b/libpod/container.go
index fda018640..8a69df685 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -437,6 +437,9 @@ type ContainerConfig struct {
// Timezone is the timezone inside the container.
// Local means it has the same timezone as the host machine
Timezone string `json:"timezone,omitempty"`
+
+ // Umask is the umask inside the container.
+ Umask string `json:"umask,omitempty"`
}
// ContainerNamedVolume is a named volume that will be mounted into the
@@ -1276,5 +1279,8 @@ func (c *Container) AutoRemove() bool {
func (c *Container) Timezone() string {
return c.config.Timezone
+}
+func (c *Container) Umask() string {
+ return c.config.Umask
}
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index bd04ee9b9..a16aea06d 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -729,10 +729,6 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi
return -1, err
}
- if exitCode != 0 {
- return exitCode, errors.Wrapf(define.ErrOCIRuntime, "exec session exited with non-zero exit code %d", exitCode)
- }
-
return exitCode, nil
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 680776dba..a0d223c8c 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -325,6 +325,14 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.Timezone = c.config.Timezone
+ // Pad Umask to 4 characters
+ if len(c.config.Umask) < 4 {
+ pad := strings.Repeat("0", 4-len(c.config.Umask))
+ ctrConfig.Umask = pad + c.config.Umask
+ } else {
+ ctrConfig.Umask = c.config.Umask
+ }
+
return ctrConfig
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 1c21f2ff9..edea62a0d 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -355,6 +355,14 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.SetProcessGID(uint32(execUser.Gid))
}
+ if c.config.Umask != "" {
+ decVal, err := strconv.ParseUint(c.config.Umask, 8, 32)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Invalid Umask Value")
+ }
+ g.SetProcessUmask(uint32(decVal))
+ }
+
// Add addition groups if c.config.GroupAdd is not empty
if len(c.config.Groups) > 0 {
gids, err := lookup.GetContainerGroups(c.config.Groups, c.state.Mountpoint, overrides)
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 64b24d9e2..6c426f2ec 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -20,6 +20,8 @@ var (
NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
// RegexError is thrown in presence of an invalid container/pod name.
RegexError = errors.Wrapf(ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*")
+ // UmaskRegex is a regular expression to validate Umask.
+ UmaskRegex = regexp.MustCompile(`^[0-7]{1,4}$`)
)
const (
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index fbd9da3e7..a08cb3de6 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -61,6 +61,8 @@ type InspectContainerConfig struct {
// systemd mode, the container configuration is customized to optimize
// running systemd in the container.
SystemdMode bool `json:"SystemdMode,omitempty"`
+ // Umask is the umask inside the container.
+ Umask string `json:"Umask,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index d341ca7b5..7c2a3e0f2 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -90,6 +90,13 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
return err
}
for {
+ select {
+ case <-ctx.Done():
+ // the consumer has cancelled
+ return nil
+ default:
+ // fallthrough
+ }
if _, err := j.Next(); err != nil {
return err
}
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index 28d0dc07e..b70102450 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -63,6 +63,14 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
}
}()
for line := range t.Lines {
+ select {
+ case <-ctx.Done():
+ // the consumer has cancelled
+ return nil
+ default:
+ // fallthrough
+ }
+
event, err := newEventFromJSONString(line.Text)
if err != nil {
return err
diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go
index b04742974..4818f8dc4 100644
--- a/libpod/healthcheck.go
+++ b/libpod/healthcheck.go
@@ -92,7 +92,7 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) {
hcResult := define.HealthCheckSuccess
config := new(ExecConfig)
config.Command = newCommand
- _, hcErr := c.Exec(config, streams, nil)
+ exitCode, hcErr := c.Exec(config, streams, nil)
if hcErr != nil {
errCause := errors.Cause(hcErr)
hcResult = define.HealthCheckFailure
@@ -104,6 +104,9 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) {
} else {
returnCode = 125
}
+ } else if exitCode != 0 {
+ hcResult = define.HealthCheckFailure
+ returnCode = 1
}
timeEnd := time.Now()
if c.HealthCheckConfig().StartPeriod > 0 {
diff --git a/libpod/options.go b/libpod/options.go
index 40cf452db..41b0d7212 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1607,6 +1607,20 @@ func WithTimezone(path string) CtrCreateOption {
}
}
+// WithUmask sets the umask in the container
+func WithUmask(umask string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ if !define.UmaskRegex.MatchString(umask) {
+ return errors.Wrapf(define.ErrInvalidArg, "Invalid umask string %s", umask)
+ }
+ ctr.config.Umask = umask
+ return nil
+ }
+}
+
// Pod Creation Options
// WithPodName sets the name of the pod.
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 5acc94153..9d5cb5045 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -1,9 +1,10 @@
package compat
import (
- "context"
+ "encoding/json"
"fmt"
"net/http"
+ "sync"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/libpod/events"
@@ -15,77 +16,132 @@ import (
"github.com/sirupsen/logrus"
)
+// filtersFromRequests extracts the "filters" parameter from the specified
+// http.Request. The paramater can either be a `map[string][]string` as done
+// in new versions of Docker and libpod, or a `map[string]map[string]bool` as
+// done in older versions of Docker. We have to do a bit of Yoga to support
+// both - just as Docker does as well.
+//
+// Please refer to https://github.com/containers/podman/issues/6899 for some
+// background.
+func filtersFromRequest(r *http.Request) ([]string, error) {
+ var (
+ compatFilters map[string]map[string]bool
+ filters map[string][]string
+ libpodFilters []string
+ )
+ raw := []byte(r.Form.Get("filters"))
+
+ // Backwards compat with older versions of Docker.
+ if err := json.Unmarshal(raw, &compatFilters); err == nil {
+ for filterKey, filterMap := range compatFilters {
+ for filterValue, toAdd := range filterMap {
+ if toAdd {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
+ }
+ }
+ }
+ return libpodFilters, nil
+ }
+
+ if err := json.Unmarshal(raw, &filters); err != nil {
+ return nil, err
+ }
+
+ for filterKey, filterSlice := range filters {
+ for _, filterValue := range filterSlice {
+ libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", filterKey, filterValue))
+ }
+ }
+
+ return libpodFilters, nil
+}
+
+// NOTE: this endpoint serves both the docker-compatible one and the new libpod
+// one.
func GetEvents(w http.ResponseWriter, r *http.Request) {
var (
- fromStart bool
- eventsError error
- decoder = r.Context().Value("decoder").(*schema.Decoder)
- runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ fromStart bool
+ decoder = r.Context().Value("decoder").(*schema.Decoder)
+ runtime = r.Context().Value("runtime").(*libpod.Runtime)
+ json = jsoniter.ConfigCompatibleWithStandardLibrary // FIXME: this should happen on the package level
)
+ // NOTE: the "filters" parameter is extracted separately for backwards
+ // compat via `fitlerFromRequest()`.
query := struct {
- Since string `schema:"since"`
- Until string `schema:"until"`
- Filters map[string][]string `schema:"filters"`
- Stream bool `schema:"stream"`
+ Since string `schema:"since"`
+ Until string `schema:"until"`
+ Stream bool `schema:"stream"`
}{
Stream: true,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
- }
-
- var libpodFilters = []string{}
- if _, found := r.URL.Query()["filters"]; found {
- for k, v := range query.Filters {
- libpodFilters = append(libpodFilters, fmt.Sprintf("%s=%s", k, v[0]))
- }
+ return
}
if len(query.Since) > 0 || len(query.Until) > 0 {
fromStart = true
}
- eventCtx, eventCancel := context.WithCancel(r.Context())
- eventChannel := make(chan *events.Event)
- go func() {
- readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until}
- eventsError = runtime.Events(eventCtx, readOpts)
- }()
- if eventsError != nil {
- utils.InternalServerError(w, eventsError)
- eventCancel()
- close(eventChannel)
+ libpodFilters, err := filtersFromRequest(r)
+ if err != nil {
+ utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
return
}
- // If client disappears we need to stop listening for events
- go func(done <-chan struct{}) {
- <-done
- eventCancel()
- if _, ok := <-eventChannel; ok {
- close(eventChannel)
+ eventChannel := make(chan *events.Event)
+ errorChannel := make(chan error)
+
+ // Start reading events.
+ go func() {
+ readOpts := events.ReadOptions{
+ FromStart: fromStart,
+ Stream: query.Stream,
+ Filters: libpodFilters,
+ EventChannel: eventChannel,
+ Since: query.Since,
+ Until: query.Until,
}
- }(r.Context().Done())
+ errorChannel <- runtime.Events(r.Context(), readOpts)
+ }()
- // Headers need to be written out before turning Writer() over to json encoder
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
- }
+ var coder *jsoniter.Encoder
+ var writeHeader sync.Once
- json := jsoniter.ConfigCompatibleWithStandardLibrary
- coder := json.NewEncoder(w)
- coder.SetEscapeHTML(true)
+ for stream := true; stream; stream = query.Stream {
+ select {
+ case err := <-errorChannel:
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ case evt := <-eventChannel:
+ writeHeader.Do(func() {
+ // Use a sync.Once so that we write the header
+ // only once.
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
+ coder = json.NewEncoder(w)
+ coder.SetEscapeHTML(true)
+ })
- for event := range eventChannel {
- e := entities.ConvertToEntitiesEvent(*event)
- if err := coder.Encode(e); err != nil {
- logrus.Errorf("unable to write json: %q", err)
- }
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
+ if evt == nil {
+ continue
+ }
+
+ e := entities.ConvertToEntitiesEvent(*evt)
+ if err := coder.Encode(e); err != nil {
+ logrus.Errorf("unable to write json: %q", err)
+ }
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
+ }
}
+
}
}
diff --git a/pkg/api/server/register_generate.go b/pkg/api/server/register_generate.go
index 82f1dc680..a1ab3f727 100644
--- a/pkg/api/server/register_generate.go
+++ b/pkg/api/server/register_generate.go
@@ -13,8 +13,8 @@ func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
// tags:
// - containers
// - pods
- // summary: Play a Kubernetes YAML file.
- // description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
+ // summary: Generate a Kubernetes YAML file.
+ // description: Generate Kubernetes YAML based on a pod or container.
// parameters:
// - in: path
// name: name:.*
diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go
index 93141400b..430184f4a 100644
--- a/pkg/bindings/test/system_test.go
+++ b/pkg/bindings/test/system_test.go
@@ -1,6 +1,7 @@
package test_bindings
import (
+ "sync"
"time"
"github.com/containers/libpod/v2/pkg/bindings"
@@ -38,22 +39,28 @@ var _ = Describe("Podman system", func() {
})
It("podman events", func() {
- eChan := make(chan entities.Event, 1)
- var messages []entities.Event
- cancelChan := make(chan bool, 1)
+ var name = "top"
+ _, err := bt.RunTopContainer(&name, bindings.PFalse, nil)
+ Expect(err).To(BeNil())
+
+ filters := make(map[string][]string)
+ filters["container"] = []string{name}
+
+ binChan := make(chan entities.Event)
+ done := sync.Mutex{}
+ done.Lock()
+ eventCounter := 0
go func() {
- for e := range eChan {
- messages = append(messages, e)
+ defer done.Unlock()
+ for range binChan {
+ eventCounter++
}
}()
- go func() {
- system.Events(bt.conn, eChan, cancelChan, nil, nil, nil, bindings.PFalse)
- }()
- _, err := bt.RunTopContainer(nil, nil, nil)
+ err = system.Events(bt.conn, binChan, nil, nil, nil, filters, bindings.PFalse)
Expect(err).To(BeNil())
- cancelChan <- true
- Expect(len(messages)).To(BeNumerically("==", 5))
+ done.Lock()
+ Expect(eventCounter).To(BeNumerically(">", 0))
})
It("podman system prune - pod,container stopped", func() {
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index f82da2c95..888811958 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -453,11 +453,16 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container
containerConfig.Command = []string{}
if imageData != nil && imageData.Config != nil {
- containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...)
+ containerConfig.Command = imageData.Config.Entrypoint
}
if len(containerYAML.Command) != 0 {
- containerConfig.Command = append(containerConfig.Command, containerYAML.Command...)
- } else if imageData != nil && imageData.Config != nil {
+ containerConfig.Command = containerYAML.Command
+ }
+ // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes
+ if len(containerYAML.Args) != 0 {
+ containerConfig.Command = append(containerConfig.Command, containerYAML.Args...)
+ } else if len(containerYAML.Command) == 0 {
+ // Add the Cmd from the image config only if containerYAML.Command and containerYAML.Args are empty
containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...)
}
if imageData != nil && len(containerConfig.Command) == 0 {
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 6dbc45c16..934d5fbac 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -145,6 +145,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Timezone != "" {
options = append(options, libpod.WithTimezone(s.Timezone))
}
+ if s.Umask != "" {
+ options = append(options, libpod.WithUmask(s.Umask))
+ }
useSystemd := false
switch s.Systemd {
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index c6079be33..84a6c36a0 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -287,6 +287,8 @@ type ContainerSecurityConfig struct {
// ReadOnlyFilesystem indicates that everything will be mounted
// as read-only
ReadOnlyFilesystem bool `json:"read_only_filesystem,omittempty"`
+ // Umask is the umask the init process of the container will be run with.
+ Umask string `json:"umask,omitempty"`
}
// ContainerCgroupConfig contains configuration information about a container's
diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf
index 0a07676c4..5f852468d 100644
--- a/test/e2e/config/containers.conf
+++ b/test/e2e/config/containers.conf
@@ -50,3 +50,5 @@ dns_servers=[ "1.2.3.4", ]
dns_options=[ "debug", ]
tz = "Pacific/Honolulu"
+
+umask = "0002"
diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go
index 23d8dd197..aebbca855 100644
--- a/test/e2e/containers_conf_test.go
+++ b/test/e2e/containers_conf_test.go
@@ -218,6 +218,17 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("HST"))
+ })
+ It("podman run containers.conf umask", func() {
+ //containers.conf umask set to 0002
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0002"))
})
+
})
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index f21f17d39..09b4f5911 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
@@ -499,4 +500,46 @@ var _ = Describe("Podman create", func() {
Expect(data[0].Config.Timezone).To(Equal("local"))
})
+ It("podman create --umask", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+
+ session := podmanTest.Podman([]string{"create", "--name", "default", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect := podmanTest.Podman([]string{"inspect", "default"})
+ inspect.WaitWithDefaultTimeout()
+ data := inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "0002", "--name", "umask", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "umask"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0002"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "0077", "--name", "fedora", fedoraMinimal})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "fedora"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0077"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "22", "--name", "umask-short", ALPINE})
+ session.WaitWithDefaultTimeout()
+ inspect = podmanTest.Podman([]string{"inspect", "umask-short"})
+ inspect.WaitWithDefaultTimeout()
+ data = inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].Config.Umask).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"create", "--umask", "9999", "--name", "bad", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask"))
+ })
})
diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go
index 93c51098f..9b527b88e 100644
--- a/test/e2e/events_test.go
+++ b/test/e2e/events_test.go
@@ -136,6 +136,7 @@ var _ = Describe("Podman events", func() {
Expect(ec).To(Equal(0))
test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"})
test.WaitWithDefaultTimeout()
+ Expect(test.ExitCode()).To(BeZero())
jsonArr := test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero()))
eventsMap := make(map[string]string)
@@ -143,10 +144,10 @@ var _ = Describe("Podman events", func() {
Expect(err).To(BeNil())
_, exist := eventsMap["Status"]
Expect(exist).To(BeTrue())
- Expect(test.ExitCode()).To(BeZero())
test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"})
test.WaitWithDefaultTimeout()
+ Expect(test.ExitCode()).To(BeZero())
jsonArr = test.OutputToStringArray()
Expect(len(jsonArr)).To(Not(BeZero()))
eventsMap = make(map[string]string)
@@ -154,6 +155,5 @@ var _ = Describe("Podman events", func() {
Expect(err).To(BeNil())
_, exist = eventsMap["Status"]
Expect(exist).To(BeTrue())
- Expect(test.ExitCode()).To(BeZero())
})
})
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 23604f47d..4b68f6232 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"text/template"
. "github.com/containers/libpod/v2/test/utils"
@@ -50,6 +51,10 @@ spec:
{{ range .Cmd }}
- {{.}}
{{ end }}
+ args:
+ {{ range .Arg }}
+ - {{.}}
+ {{ end }}
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
@@ -129,6 +134,10 @@ spec:
{{ range .Cmd }}
- {{.}}
{{ end }}
+ args:
+ {{ range .Arg }}
+ - {{.}}
+ {{ end }}
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
@@ -171,6 +180,7 @@ spec:
var (
defaultCtrName = "testCtr"
defaultCtrCmd = []string{"top"}
+ defaultCtrArg = []string{"-d", "1.5"}
defaultCtrImage = ALPINE
defaultPodName = "testPod"
defaultDeploymentName = "testDeployment"
@@ -322,6 +332,7 @@ type Ctr struct {
Name string
Image string
Cmd []string
+ Arg []string
SecurityContext bool
Caps bool
CapAdd []string
@@ -332,7 +343,7 @@ type Ctr struct {
// getCtr takes a list of ctrOptions and returns a Ctr with sane defaults
// and the configured options
func getCtr(options ...ctrOption) *Ctr {
- c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil, ""}
+ c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, defaultCtrArg, true, false, nil, nil, ""}
for _, option := range options {
option(&c)
}
@@ -347,6 +358,12 @@ func withCmd(cmd []string) ctrOption {
}
}
+func withArg(arg []string) ctrOption {
+ return func(c *Ctr) {
+ c.Arg = arg
+ }
+}
+
func withImage(img string) ctrOption {
return func(c *Ctr) {
c.Image = img
@@ -438,14 +455,50 @@ var _ = Describe("Podman generate kube", func() {
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ // Use the defined command to override the image's command
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
+ })
+
+ It("podman play kube test correct command with only set command in yaml file", func() {
+ pod := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg(nil))))
+ err := generatePodKubeYaml(pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ // Use the defined command to override the image's command, and don't set the args
+ // so the full command in result should not contains the image's command
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`))
+ })
+
+ It("podman play kube test correct command with only set args in yaml file", func() {
+ pod := getPod(withCtr(getCtr(withImage(redis), withCmd(nil), withArg([]string{"echo", "hello"}))))
+ err := generatePodKubeYaml(pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ // this image's ENTRYPOINT is called `docker-entrypoint.sh`
+ // so result should be `docker-entrypoint.sh + withArg(...)`
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[docker-entrypoint.sh echo hello]`))
})
It("podman play kube test correct output", func() {
- p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}))))
+ p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg([]string{"world"}))))
err := generatePodKubeYaml(p, kubeYaml)
Expect(err).To(BeNil())
@@ -457,12 +510,12 @@ var _ = Describe("Podman generate kube", func() {
logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(p)})
logs.WaitWithDefaultTimeout()
Expect(logs.ExitCode()).To(Equal(0))
- Expect(logs.OutputToString()).To(ContainSubstring("hello"))
+ Expect(logs.OutputToString()).To(ContainSubstring("hello world"))
inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring("hello"))
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello world]`))
})
It("podman play kube test hostname", func() {
@@ -498,7 +551,7 @@ var _ = Describe("Podman generate kube", func() {
It("podman play kube cap add", func() {
capAdd := "CAP_SYS_ADMIN"
- ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}))
+ ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}), withArg(nil))
pod := getPod(withCtr(ctr))
err := generatePodKubeYaml(pod, kubeYaml)
@@ -556,7 +609,7 @@ var _ = Describe("Podman generate kube", func() {
}
ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName
- ctr := getCtr(withCmd([]string{"pwd"}))
+ ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil))
pod := getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile)))
err = generatePodKubeYaml(pod, kubeYaml)
@@ -582,7 +635,7 @@ var _ = Describe("Podman generate kube", func() {
}
defer os.Remove(jsonFile)
- ctr := getCtr(withCmd([]string{"pwd"}))
+ ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil))
pod := getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile)))
err = generatePodKubeYaml(pod, kubeYaml)
@@ -734,10 +787,12 @@ spec:
Expect(kube.ExitCode()).To(Equal(0))
podNames := getPodNamesInDeployment(deployment)
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0])})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ // yaml's command shuold override the image's Entrypoint
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
})
It("podman play kube deployment more than 1 replica test correct command", func() {
@@ -752,11 +807,12 @@ spec:
Expect(kube.ExitCode()).To(Equal(0))
podNames := getPodNamesInDeployment(deployment)
+ correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ")
for i = 0; i < numReplicas; i++ {
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i])})
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(0))
- Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0]))
+ Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
}
})
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 9d48f1540..7bb474769 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1081,4 +1081,35 @@ USER mail`
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(limit))
})
+
+ It("podman run umask", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "crun") {
+ Skip("Test only works on crun")
+ }
+
+ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "0002", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0002"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "0077", "--rm", fedoraMinimal, "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0077"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "22", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal("0022"))
+
+ session = podmanTest.Podman([]string{"run", "--umask", "9999", "--rm", ALPINE, "sh", "-c", "umask"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask"))
+ })
})
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index 3d05b44fe..76d29d22c 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -78,7 +78,8 @@ function check_help() {
if ! expr "$usage" : '.*[A-Z]' >/dev/null; then
if [ "$cmd" != "help" ]; then
dprint "$command_string invalid-arg"
- run_podman 125 "$@" $cmd invalid-arg
+ run_podman '?' "$@" $cmd invalid-arg
+ is "$status" 125 "'$command_string invalid-arg' - exit status"
is "$output" "Error: .* takes no arguments" \
"'$command_string' with extra (invalid) arguments"
fi
@@ -104,7 +105,8 @@ function check_help() {
# The </dev/null protects us from 'podman login' which will
# try to read username/password from stdin.
dprint "$command_string (without required args)"
- run_podman 125 "$@" $cmd </dev/null
+ run_podman '?' "$@" $cmd </dev/null
+ is "$status" 125 "'$command_string' with no arguments - exit status"
is "$output" "Error:.* \(require\|specif\|must\|provide\|need\|choose\|accepts\)" \
"'$command_string' without required arg"
@@ -126,7 +128,8 @@ function check_help() {
local rhs=$(sed -e 's/^[^A-Z]\+[A-Z]/X/' -e 's/ | /-or-/g' <<<"$usage")
local n_args=$(wc -w <<<"$rhs")
- run_podman 125 "$@" $cmd $(seq --format='x%g' 0 $n_args)
+ run_podman '?' "$@" $cmd $(seq --format='x%g' 0 $n_args)
+ is "$status" 125 "'$command_string' with >$n_args arguments - exit status"
is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \
"'$command_string' with >$n_args arguments"
@@ -140,13 +143,17 @@ function check_help() {
# Any command that takes subcommands, must throw error if called
# without one.
dprint "podman $@"
- run_podman 125 "$@"
- is "$output" "Error: missing command .*$@ COMMAND"
+ run_podman '?' "$@"
+ is "$status" 125 "'podman $*' without any subcommand - exit status"
+ is "$output" "Error: missing command .*$@ COMMAND" \
+ "'podman $*' without any subcommand - expected error message"
# Assume that 'NoSuchCommand' is not a command
dprint "podman $@ NoSuchCommand"
- run_podman 125 "$@" NoSuchCommand
- is "$output" "Error: unrecognized command .*$@ NoSuchCommand"
+ run_podman '?' "$@" NoSuchCommand
+ is "$status" 125 "'podman $* NoSuchCommand' - exit status"
+ is "$output" "Error: unrecognized command .*$@ NoSuchCommand" \
+ "'podman $* NoSuchCommand' - expected error message"
# This can happen if the output of --help changes, such as between
# the old command parser and cobra.
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
index 6d3268902..c757c20e0 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
@@ -29,6 +29,9 @@ var (
type Generator struct {
Config *rspec.Spec
HostSpecific bool
+ // This is used to keep a cache of the ENVs added to improve
+ // performance when adding a huge number of ENV variables
+ envMap map[string]int
}
// ExportOptions have toggles for exporting only certain parts of the specification
@@ -236,7 +239,12 @@ func New(os string) (generator Generator, err error) {
}
}
- return Generator{Config: &config}, nil
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
+ return Generator{Config: &config, envMap: envCache}, nil
}
// NewFromSpec creates a configuration Generator from a given
@@ -246,8 +254,14 @@ func New(os string) (generator Generator, err error) {
//
// generator := Generator{Config: config}
func NewFromSpec(config *rspec.Spec) Generator {
+ envCache := map[string]int{}
+ if config != nil && config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: config,
+ envMap: envCache,
}
}
@@ -273,11 +287,27 @@ func NewFromTemplate(r io.Reader) (Generator, error) {
if err := json.NewDecoder(r).Decode(&config); err != nil {
return Generator{}, err
}
+
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: &config,
+ envMap: envCache,
}, nil
}
+// createEnvCacheMap creates a hash map with the ENV variables given by the config
+func createEnvCacheMap(env []string) map[string]int {
+ envMap := make(map[string]int, len(env))
+ for i, val := range env {
+ envMap[val] = i
+ }
+ return envMap
+}
+
// SetSpec sets the configuration in the Generator g.
//
// Deprecated: Replace with:
@@ -414,6 +444,12 @@ func (g *Generator) SetProcessUsername(username string) {
g.Config.Process.User.Username = username
}
+// SetProcessUmask sets g.Config.Process.User.Umask.
+func (g *Generator) SetProcessUmask(umask uint32) {
+ g.initConfigProcess()
+ g.Config.Process.User.Umask = umask
+}
+
// SetProcessGID sets g.Config.Process.User.GID.
func (g *Generator) SetProcessGID(gid uint32) {
g.initConfigProcess()
@@ -456,21 +492,44 @@ func (g *Generator) ClearProcessEnv() {
return
}
g.Config.Process.Env = []string{}
+ // Clear out the env cache map as well
+ g.envMap = map[string]int{}
}
// AddProcessEnv adds name=value into g.Config.Process.Env, or replaces an
// existing entry with the given name.
func (g *Generator) AddProcessEnv(name, value string) {
+ if name == "" {
+ return
+ }
+
g.initConfigProcess()
+ g.addEnv(fmt.Sprintf("%s=%s", name, value), name)
+}
- env := fmt.Sprintf("%s=%s", name, value)
- for idx := range g.Config.Process.Env {
- if strings.HasPrefix(g.Config.Process.Env[idx], name+"=") {
- g.Config.Process.Env[idx] = env
- return
- }
+// AddMultipleProcessEnv adds multiple name=value into g.Config.Process.Env, or replaces
+// existing entries with the given name.
+func (g *Generator) AddMultipleProcessEnv(envs []string) {
+ g.initConfigProcess()
+
+ for _, val := range envs {
+ split := strings.SplitN(val, "=", 2)
+ g.addEnv(val, split[0])
+ }
+}
+
+// addEnv looks through adds ENV to the Process and checks envMap for
+// any duplicates
+// This is called by both AddMultipleProcessEnv and AddProcessEnv
+func (g *Generator) addEnv(env, key string) {
+ if idx, ok := g.envMap[key]; ok {
+ // The ENV exists in the cache, so change its value in g.Config.Process.Env
+ g.Config.Process.Env[idx] = env
+ } else {
+ // else the env doesn't exist, so add it and add it's index to g.envMap
+ g.Config.Process.Env = append(g.Config.Process.Env, env)
+ g.envMap[key] = len(g.Config.Process.Env) - 1
}
- g.Config.Process.Env = append(g.Config.Process.Env, env)
}
// AddProcessRlimits adds rlimit into g.Config.Process.Rlimits.
@@ -1443,7 +1502,7 @@ func (g *Generator) AddDevice(device rspec.LinuxDevice) {
return
}
if dev.Type == device.Type && dev.Major == device.Major && dev.Minor == device.Minor {
- fmt.Fprintln(os.Stderr, "WARNING: The same type, major and minor should not be used for multiple devices.")
+ fmt.Fprintf(os.Stderr, "WARNING: Creating device %q with same type, major and minor as existing %q.\n", device.Path, dev.Path)
}
}
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
index 5fee5a3b2..8a8dc3970 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
@@ -566,6 +566,20 @@ func DefaultProfile(rs *specs.Spec) *rspec.LinuxSeccomp {
},
}...)
/* Flags parameter of the clone syscall is the 2nd on s390 */
+ syscalls = append(syscalls, []rspec.LinuxSyscall{
+ {
+ Names: []string{"clone"},
+ Action: rspec.ActAllow,
+ Args: []rspec.LinuxSeccompArg{
+ {
+ Index: 1,
+ Value: 2080505856,
+ ValueTwo: 0,
+ Op: rspec.OpMaskedEqual,
+ },
+ },
+ },
+ }...)
}
return &rspec.LinuxSeccomp{
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 913cb71eb..4d10cd5b8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -421,7 +421,7 @@ github.com/opencontainers/runc/libcontainer/user
github.com/opencontainers/runc/libcontainer/utils
# github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2
github.com/opencontainers/runtime-spec/specs-go
-# github.com/opencontainers/runtime-tools v0.9.0
+# github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa
github.com/opencontainers/runtime-tools/error
github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/generate