summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2020-07-23 11:04:48 +0200
committerGitHub <noreply@github.com>2020-07-23 11:04:48 +0200
commitdade9159e7a0f848e101221274faa3f4f03cf132 (patch)
tree4fc8aa7a9379260c103d86c37cfc0a2ad511a9e9
parent2d24487ba244e5cd900f6aecc5d8896e1354d1ee (diff)
parent2faeb2189f81b6925d80aa8031cb5b19aa8618cb (diff)
downloadpodman-dade9159e7a0f848e101221274faa3f4f03cf132.tar.gz
podman-dade9159e7a0f848e101221274faa3f4f03cf132.tar.bz2
podman-dade9159e7a0f848e101221274faa3f4f03cf132.zip
Merge pull request #7054 from mheon/backports_203
Backports and Release Notes for v2.0.3
-rw-r--r--RELEASE_NOTES.md38
-rw-r--r--cmd/podman/common/create.go6
-rw-r--r--cmd/podman/common/specgen.go15
-rw-r--r--cmd/podman/containers/start.go1
-rw-r--r--cmd/podman/early_init_linux.go39
-rw-r--r--cmd/podman/early_init_unsupported.go6
-rw-r--r--cmd/podman/images/list.go53
-rw-r--r--cmd/podman/images/pull.go1
-rw-r--r--cmd/podman/images/push.go1
-rw-r--r--cmd/podman/parse/json.go9
-rw-r--r--cmd/podman/parse/json_test.go30
-rw-r--r--cmd/podman/pods/create.go2
-rw-r--r--cmd/podman/root.go1
-rw-r--r--cmd/podman/system/info.go3
-rw-r--r--cmd/podman/system/version.go3
-rwxr-xr-xcontrib/cirrus/setup_environment.sh1
-rw-r--r--docs/source/markdown/podman-build.1.md9
-rw-r--r--docs/source/markdown/podman-create.1.md19
-rw-r--r--docs/source/markdown/podman-image-trust.1.md4
-rw-r--r--docs/source/markdown/podman-pod-create.1.md2
-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.md21
-rw-r--r--libpod/container_api.go14
-rw-r--r--libpod/container_exec.go20
-rw-r--r--libpod/container_inspect.go18
-rw-r--r--libpod/container_internal.go44
-rw-r--r--libpod/container_internal_linux.go23
-rw-r--r--libpod/container_internal_unsupported.go2
-rw-r--r--libpod/container_log.go23
-rw-r--r--libpod/container_log_linux.go16
-rw-r--r--libpod/container_log_unsupported.go4
-rw-r--r--libpod/define/config.go10
-rw-r--r--libpod/define/container_inspect.go57
-rw-r--r--libpod/define/ctr_inspect.go54
-rw-r--r--libpod/define/pod_inspect.go6
-rw-r--r--libpod/events/events_linux.go10
-rw-r--r--libpod/networking_linux.go34
-rw-r--r--libpod/networking_unsupported.go6
-rw-r--r--libpod/oci_conmon_linux.go9
-rw-r--r--libpod/options.go8
-rw-r--r--libpod/pod_api.go41
-rw-r--r--libpod/runtime.go17
-rw-r--r--libpod/runtime_ctr.go22
-rw-r--r--libpod/state_test.go8
-rw-r--r--libpod/storage.go8
-rw-r--r--libpod/util.go19
-rw-r--r--pkg/api/handlers/compat/containers_attach.go79
-rw-r--r--pkg/api/handlers/compat/containers_create.go33
-rw-r--r--pkg/api/handlers/compat/containers_logs.go76
-rw-r--r--pkg/api/handlers/compat/exec.go14
-rw-r--r--pkg/api/server/handler_api.go1
-rw-r--r--pkg/api/server/idletracker/idletracker.go74
-rw-r--r--pkg/api/server/register_generate.go4
-rw-r--r--pkg/api/server/server.go72
-rw-r--r--pkg/bindings/containers/attach.go8
-rw-r--r--pkg/domain/entities/pods.go2
-rw-r--r--pkg/domain/infra/abi/containers.go2
-rw-r--r--pkg/domain/infra/abi/play.go11
-rw-r--r--pkg/domain/infra/abi/system.go41
-rw-r--r--pkg/spec/createconfig.go4
-rw-r--r--pkg/spec/spec.go15
-rw-r--r--pkg/specgen/container_validate.go13
-rw-r--r--pkg/specgen/generate/config_linux.go1
-rw-r--r--pkg/specgen/generate/container_create.go16
-rw-r--r--pkg/specgen/generate/oci.go30
-rw-r--r--pkg/specgen/generate/pod_create.go4
-rw-r--r--pkg/specgen/generate/ports.go1
-rw-r--r--pkg/specgen/generate/security.go30
-rw-r--r--pkg/specgen/pod_validate.go11
-rw-r--r--pkg/specgen/podspecgen.go6
-rw-r--r--pkg/specgen/specgen.go17
-rw-r--r--pkg/util/utils.go4
-rw-r--r--pkg/varlinkapi/containers.go2
-rw-r--r--pkg/varlinkapi/create.go4
-rw-r--r--rootless.md2
-rw-r--r--test/e2e/common_test.go4
-rw-r--r--test/e2e/create_staticip_test.go27
-rw-r--r--test/e2e/create_staticmac_test.go12
-rw-r--r--test/e2e/exec_test.go1
-rw-r--r--test/e2e/info_test.go30
-rw-r--r--test/e2e/libpod_suite_remote_test.go5
-rw-r--r--test/e2e/play_kube_test.go82
-rw-r--r--test/e2e/pod_create_test.go35
-rw-r--r--test/e2e/pod_inspect_test.go21
-rw-r--r--test/e2e/run_apparmor_test.go158
-rw-r--r--test/e2e/run_entrypoint_test.go5
-rw-r--r--test/e2e/run_networking_test.go14
-rw-r--r--test/e2e/system_df_test.go14
-rw-r--r--test/e2e/systemd_test.go35
-rw-r--r--test/e2e/version_test.go34
-rw-r--r--test/system/200-pod.bats8
92 files changed, 1273 insertions, 538 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 898a09d70..96ee8a02c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,43 @@
# Release Notes
+## 2.0.3
+### Features
+- The `podman search` command now allows wildcards in search terms.
+- The `podman play kube` command now supports the `IfNotPresent` pull type.
+
+### Changes
+- The `--disable-content-trust` flag has been added to Podman for Docker compatibility. This is a Docker-specific option and has no effect in Podman; it is provided only to ensure command line compatibility for scripts ([#7034](https://github.com/containers/podman/issues/7034)).
+- Setting a static IP address or MAC address for rootless containers and pods now causes an error; previously, they were silently ignored.
+- The `/sys/dev` folder is now masked in containers to prevent a potential information leak from the host.
+
+### Bugfixes
+- Fixed a bug where rootless Podman would select the wrong cgroup manager on cgroups v1 systems where the user in question had an active systemd user session ([#6982](https://github.com/containers/podman/issues/6982)).
+- Fixed a bug where systems with Apparmor could not run privileged containers ([#6933](https://github.com/containers/podman/issues/6933)).
+- Fixed a bug where ENTRYPOINT and CMD from images were improperly handled by `podman play kube` ([#6995](https://github.com/containers/podman/issues/6995)).
+- Fixed a bug where the `--pids-limit` flag to `podman create` and `podman run` was parsed incorrectly and was unusable ([#6908](https://github.com/containers/podman/issues/6908)).
+- Fixed a bug where the `podman system df` command would error if untagged images were present ([#7015](https://github.com/containers/podman/issues/7015)).
+- Fixed a bug where the `podman images` command would display incorrect tags if a port number was included in the repository.
+- Fixed a bug where Podman did not set a default umask and default rlimits ([#6989](https://github.com/containers/podman/issues/6989)).
+- Fixed a bug where protocols in port mappings were not recognized unless they were lower-case ([#6948](https://github.com/containers/podman/issues/6948)).
+- Fixed a bug where information on pod infra containers was not included in the output of `podman pod inspect`.
+- Fixed a bug where Podman's systemd detection (activated by the enabled-by-default `--systemd=true` flag) would not flag a container for systemd mode if systemd was part of the entrypoint, not the command ([#6920](https://github.com/containers/podman/issues/6920)).
+- Fixed a bug where `podman start --attach` was not defaulting `--sig-proxy` to true ([#6928](https://github.com/containers/podman/issues/6928)).
+- Fixed a bug where `podman inspect` would show an incorrect command (`podman system service`, the command used to start the server) for containers created by a remote Podman client.
+- Fixed a bug where the `podman exec` command with the remote client would not print output if the `-t` or `-i` flags where not provided.
+- Fixed a bug where some variations of the `--format {{ json . }}` to `podman info` (involving added or removed whitespace) would not be accepted ([#6927](https://github.com/containers/podman/issues/6927)).
+- Fixed a bug where Entrypoint could not be cleared at the command line (if unset via `--entrypoint=""`, it would be reset to the image's entrypoint) ([#6935](https://github.com/containers/podman/issues/6935)).
+
+### API
+- Fixed a bug where the events endpoints (both libpod and compat) could potentially panic on parsing filters.
+- Fixed a bug where the compat Create endpoint for containers did not properly handle Entrypoint and Command.
+- Fixed a bug where the Logs endpoint for containers (both libpod and compat) would not properly handle client disconnect, resulting in high CPU usage.
+- The type of filters on the compat events endpoint has been adjusted to match Docker's implementation ([#6899](https://github.com/containers/podman/issues/6899)).
+- The idle connection counter now properly handles hijacked connections.
+- All endpoints that hijack will now properly print headers per RFC 7230 standards.
+
+### Misc
+- Updated containers/common to v0.14.6
+
## 2.0.2
### Changes
- The `podman system connection` command has been temporarily disabled, as it was not functioning as expected.
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index ee7f957cc..5f3198ed6 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -154,6 +154,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",
)
@@ -395,7 +399,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
)
createFlags.StringArrayVar(
&cf.SecurityOpt,
- "security-opt", containerConfig.SecurityOptions(),
+ "security-opt", []string{},
"Security Options",
)
createFlags.String(
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index b4f786da2..aa8669e7a 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -365,9 +365,10 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Annotations = annotations
s.WorkDir = c.Workdir
- entrypoint := []string{}
userCommand := []string{}
+ var command []string
if c.Entrypoint != nil {
+ entrypoint := []string{}
if ep := *c.Entrypoint; len(ep) > 0 {
// Check if entrypoint specified is json
if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil {
@@ -375,14 +376,14 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
}
}
s.Entrypoint = entrypoint
- }
- var command []string
-
- // Build the command
- // If we have an entry point, it goes first
- if c.Entrypoint != nil {
+ // Build the command
+ // If we have an entry point, it goes first
command = entrypoint
}
+
+ // Include the command used to create the container.
+ s.ContainerCreateCommand = os.Args
+
if len(inputCommand) > 0 {
// User command overrides data CMD
command = append(command, inputCommand...)
diff --git a/cmd/podman/containers/start.go b/cmd/podman/containers/start.go
index 941588137..21f22b986 100644
--- a/cmd/podman/containers/start.go
+++ b/cmd/podman/containers/start.go
@@ -82,6 +82,7 @@ func start(cmd *cobra.Command, args []string) error {
if cmd.Flag("sig-proxy").Changed {
sigProxy = startOptions.SigProxy
}
+ startOptions.SigProxy = sigProxy
if sigProxy && !startOptions.Attach {
return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach")
diff --git a/cmd/podman/early_init_linux.go b/cmd/podman/early_init_linux.go
new file mode 100644
index 000000000..b43450a7f
--- /dev/null
+++ b/cmd/podman/early_init_linux.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+
+ "github.com/containers/libpod/v2/libpod/define"
+ "github.com/pkg/errors"
+)
+
+func setRLimits() error {
+ rlimits := new(syscall.Rlimit)
+ rlimits.Cur = define.RLimitDefaultValue
+ rlimits.Max = define.RLimitDefaultValue
+ if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
+ if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
+ return errors.Wrapf(err, "error getting rlimits")
+ }
+ rlimits.Cur = rlimits.Max
+ if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
+ return errors.Wrapf(err, "error setting new rlimits")
+ }
+ }
+ return nil
+}
+
+func setUMask() {
+ // Be sure we can create directories with 0755 mode.
+ syscall.Umask(0022)
+}
+
+func earlyInitHook() {
+ if err := setRLimits(); err != nil {
+ fmt.Fprint(os.Stderr, "Failed to set rlimits: "+err.Error())
+ }
+
+ setUMask()
+}
diff --git a/cmd/podman/early_init_unsupported.go b/cmd/podman/early_init_unsupported.go
new file mode 100644
index 000000000..4e748559f
--- /dev/null
+++ b/cmd/podman/early_init_unsupported.go
@@ -0,0 +1,6 @@
+// +build !linux
+
+package main
+
+func earlyInitHook() {
+}
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index 8d50986d5..94d03bd6f 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -1,7 +1,6 @@
package images
import (
- "errors"
"fmt"
"os"
"sort"
@@ -11,9 +10,11 @@ import (
"time"
"unicode"
+ "github.com/containers/image/v5/docker/reference"
"github.com/containers/libpod/v2/cmd/podman/registry"
"github.com/containers/libpod/v2/pkg/domain/entities"
"github.com/docker/go-units"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -98,7 +99,10 @@ func images(cmd *cobra.Command, args []string) error {
return err
}
- imgs := sortImages(summaries)
+ imgs, err := sortImages(summaries)
+ if err != nil {
+ return err
+ }
switch {
case listFlag.quiet:
return writeID(imgs)
@@ -170,14 +174,18 @@ func writeTemplate(imgs []imageReporter) error {
return tmpl.Execute(w, imgs)
}
-func sortImages(imageS []*entities.ImageSummary) []imageReporter {
+func sortImages(imageS []*entities.ImageSummary) ([]imageReporter, error) {
imgs := make([]imageReporter, 0, len(imageS))
+ var err error
for _, e := range imageS {
var h imageReporter
if len(e.RepoTags) > 0 {
for _, tag := range e.RepoTags {
h.ImageSummary = *e
- h.Repository, h.Tag = tokenRepoTag(tag)
+ h.Repository, h.Tag, err = tokenRepoTag(tag)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing repository tag %q:", tag)
+ }
imgs = append(imgs, h)
}
} else {
@@ -189,23 +197,32 @@ func sortImages(imageS []*entities.ImageSummary) []imageReporter {
}
sort.Slice(imgs, sortFunc(listFlag.sort, imgs))
- return imgs
+ return imgs, err
}
-func tokenRepoTag(tag string) (string, string) {
- tokens := strings.Split(tag, ":")
- switch len(tokens) {
- case 0:
- return tag, ""
- case 1:
- return tokens[0], ""
- case 2:
- return tokens[0], tokens[1]
- case 3:
- return tokens[0] + ":" + tokens[1], tokens[2]
- default:
- return "<N/A>", ""
+func tokenRepoTag(ref string) (string, string, error) {
+
+ if ref == "<none>:<none>" {
+ return "<none>", "<none>", nil
+ }
+
+ repo, err := reference.Parse(ref)
+ if err != nil {
+ return "", "", err
+ }
+
+ named, ok := repo.(reference.Named)
+ if !ok {
+ return ref, "", nil
}
+
+ tagged, ok := repo.(reference.Tagged)
+ if !ok {
+ return named.Name(), "", nil
+ }
+
+ return named.Name(), tagged.Tag(), nil
+
}
func sortFunc(key string, data []imageReporter) func(i, j int) bool {
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 fd91ab0ab..c25c33784 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -77,6 +77,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/cmd/podman/parse/json.go b/cmd/podman/parse/json.go
new file mode 100644
index 000000000..95a6633b8
--- /dev/null
+++ b/cmd/podman/parse/json.go
@@ -0,0 +1,9 @@
+package parse
+
+import "regexp"
+
+var jsonFormatRegex = regexp.MustCompile(`^(\s*json\s*|\s*{{\s*json\s*\.\s*}}\s*)$`)
+
+func MatchesJSONFormat(s string) bool {
+ return jsonFormatRegex.Match([]byte(s))
+}
diff --git a/cmd/podman/parse/json_test.go b/cmd/podman/parse/json_test.go
new file mode 100644
index 000000000..5cad185fd
--- /dev/null
+++ b/cmd/podman/parse/json_test.go
@@ -0,0 +1,30 @@
+package parse
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMatchesJSONFormat(t *testing.T) {
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"json", true},
+ {" json", true},
+ {"json ", true},
+ {" json ", true},
+ {"{{json .}}", true},
+ {"{{ json .}}", true},
+ {"{{json . }}", true},
+ {" {{ json . }} ", true},
+ {"{{json }}", false},
+ {"{{json .", false},
+ {"json . }}", false},
+ }
+
+ for _, tt := range tests {
+ assert.Equal(t, tt.expected, MatchesJSONFormat(tt.input))
+ }
+}
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 0e2a085fd..d57a2f2f7 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -149,6 +149,8 @@ func create(cmd *cobra.Command, args []string) error {
}
}
+ createOptions.CreateCommand = os.Args
+
if replace {
if err := replacePod(createOptions.Name); err != nil {
return err
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 7c54da91a..b2c9f9c2c 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -77,6 +77,7 @@ func init() {
cobra.OnInitialize(
loggingHook,
syslogHook,
+ earlyInitHook,
)
rootFlags(rootCmd, registry.PodmanConfig())
diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go
index 699f7b55c..410b3455a 100644
--- a/cmd/podman/system/info.go
+++ b/cmd/podman/system/info.go
@@ -5,6 +5,7 @@ import (
"os"
"text/template"
+ "github.com/containers/libpod/v2/cmd/podman/parse"
"github.com/containers/libpod/v2/cmd/podman/registry"
"github.com/containers/libpod/v2/cmd/podman/validate"
"github.com/containers/libpod/v2/pkg/domain/entities"
@@ -68,7 +69,7 @@ func info(cmd *cobra.Command, args []string) error {
return err
}
- if inFormat == "json" {
+ if parse.MatchesJSONFormat(inFormat) {
b, err := json.MarshalIndent(info, "", " ")
if err != nil {
return err
diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go
index 5aac34699..9b70bc9f4 100644
--- a/cmd/podman/system/version.go
+++ b/cmd/podman/system/version.go
@@ -8,6 +8,7 @@ import (
"text/tabwriter"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/v2/cmd/podman/parse"
"github.com/containers/libpod/v2/cmd/podman/registry"
"github.com/containers/libpod/v2/cmd/podman/validate"
"github.com/containers/libpod/v2/libpod/define"
@@ -41,7 +42,7 @@ func version(cmd *cobra.Command, args []string) error {
}
switch {
- case versionFormat == "json", versionFormat == "{{ json .}}":
+ case parse.MatchesJSONFormat(versionFormat):
s, err := json.MarshalToString(versions)
if err != nil {
return err
diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh
index ea2c7d8e0..fbdae83fa 100755
--- a/contrib/cirrus/setup_environment.sh
+++ b/contrib/cirrus/setup_environment.sh
@@ -41,7 +41,6 @@ case "${OS_RELEASE_ID}" in
ubuntu)
apt-get update
apt-get install -y containers-common
- sed -ie 's/^\(# \)\?apparmor_profile =.*/apparmor_profile = ""/' /etc/containers/containers.conf
if [[ "$OS_RELEASE_VER" == "19" ]]; then
apt-get purge -y --auto-remove golang*
apt-get install -y golang-1.13
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index dc38caac0..24225bcb7 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -577,7 +577,7 @@ process.
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the Podman
- container. The `OPTIONS` are a comma delimited list and can be:
+ container. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
* [rw|ro]
* [z|Z|O]
@@ -640,7 +640,7 @@ be specified only for bind mounted volumes and not for internal volumes or
named volumes. For mount propagation to work on the source mount point (mount point
where source dir is mounted on) has to have the right propagation properties. For
shared volumes, the source mount point has to be shared. And for slave volumes,
-the source mount has to be either shared or slave.
+the source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
Use `df <source-dir>` to determine the source mount and then use
`findmnt -o TARGET,PROPAGATION <source-mount-dir>` to determine propagation
@@ -648,7 +648,7 @@ properties of source mount, if `findmnt` utility is not available, the source mo
can be determined by looking at the mount entry in `/proc/self/mountinfo`. Look
at `optional fields` and see if any propagation properties are specified.
`shared:X` means the mount is `shared`, `master:X` means the mount is `slave` and if
-nothing is there that means the mount is `private`.
+nothing is there that means the mount is `private`. <sup>[[1]](#Footnote1)</sup>
To change propagation properties of a mount point use the `mount` command. For
example, to bind mount the source directory `/foo` do
@@ -752,3 +752,6 @@ podman(1), buildah(1), containers-registries.conf(5), crun(8), runc(8), useradd(
May 2018, Minor revisions added by Joe Doss <joe@solidadmin.com>
December 2017, Originally compiled by Tom Sweeney <tsweeney@redhat.com>
+
+## FOOTNOTES
+<a name="Footnote1">1</a>: The Podman project is committed to inclusivity, a core value of open source. The `master` and `slave` mount propagation terminology used here is problematic and divisive, and should be changed. However, these terms are currently used within the Linux kernel and must be used as-is at this time. When the kernel maintainers rectify this usage, Podman will follow suit immediately.
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 6d8686d24..2b383f51b 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -231,6 +231,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>'.
@@ -485,7 +491,7 @@ Tune a container's memory swappiness behavior. Accepts an integer between 0 and
Attach a filesystem mount to the container
-Current supported mount TYPES are `bind`, `volume`, and `tmpfs`.
+Current supported mount TYPES are `bind`, `volume`, and `tmpfs`. <sup>[[1]](#Footnote1)</sup>
e.g.
@@ -866,7 +872,7 @@ Set the UTS mode for the container
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman
-container. The `OPTIONS` are a comma delimited list and can be:
+container. The `OPTIONS` are a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
* [rw|ro]
* [z|Z]
@@ -918,7 +924,7 @@ this behavior by specifying a volume mount propagation property. Making a
volume `shared` mounts done under that volume inside container will be
visible on host and vice versa. Making a volume `slave` enables only one
way mount propagation and that is mounts done on host under that volume
-will be visible inside container but not the other way around.
+will be visible inside container but not the other way around. <sup>[[1]](#Footnote1)</sup>
To control mount propagation property of volume one can use `:[r]shared`,
`:[r]slave` or `:[r]private` propagation flag. Propagation property can
@@ -926,7 +932,7 @@ be specified only for bind mounted volumes and not for internal volumes or
named volumes. For mount propagation to work source mount point (mount point
where source dir is mounted on) has to have right propagation properties. For
shared volumes, source mount point has to be shared. And for slave volumes,
-source mount has to be either shared or slave.
+source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
If you want to recursively mount a volume and all of its submounts into a
container, then you can use the `rbind` option. By default the bind option is
@@ -953,7 +959,7 @@ properties of source mount. If `findmnt` utility is not available, then one
can look at mount entry for source mount point in `/proc/self/mountinfo`. Look
at `optional fields` and see if any propagation properties are specified.
`shared:X` means mount is `shared`, `master:X` means mount is `slave` and if
-nothing is there that means mount is `private`.
+nothing is there that means mount is `private`. <sup>[[1]](#Footnote1)</sup>
To change propagation properties of a mount point use `mount` command. For
example, if one wants to bind mount source directory `/foo` one can do
@@ -1091,3 +1097,6 @@ November 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
September 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
August 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
+
+## FOOTNOTES
+<a name="Footnote1">1</a>: The Podman project is committed to inclusivity, a core value of open source. The `master` and `slave` mount propagation terminology used here is problematic and divisive, and should be changed. However, these terms are currently used within the Linux kernel and must be used as-is at this time. When the kernel maintainers rectify this usage, Podman will follow suit immediately.
diff --git a/docs/source/markdown/podman-image-trust.1.md b/docs/source/markdown/podman-image-trust.1.md
index 435d117f1..8b80ca602 100644
--- a/docs/source/markdown/podman-image-trust.1.md
+++ b/docs/source/markdown/podman-image-trust.1.md
@@ -30,8 +30,8 @@ If no configuration is found for any of these scopes, the default value (specifi
Trust **type** provides a way to:
-Whitelist ("accept") or
-Blacklist ("reject") registries or
+Allowlist ("accept") or
+Denylist ("reject") registries or
Require signature (“signedBy”).
Trust may be updated using the command **podman image trust set** for an existing trust scope.
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index 1401400bb..d60fc65fe 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -108,7 +108,7 @@ If another pod with the same name already exists, replace and remove it. The de
**--share**=*namespace*
-A comma delimited list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts.
+A comma delimited list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, uts.
The operator can identify a pod in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
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 6632ac2d1..7b93eb025 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -245,6 +245,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_.
@@ -493,7 +499,7 @@ Tune a container's memory swappiness behavior. Accepts an integer between *0* an
Attach a filesystem mount to the container
-Current supported mount TYPEs are **bind**, **volume**, and **tmpfs**.
+Current supported mount TYPEs are **bind**, **volume**, and **tmpfs**. <sup>[[1]](#Footnote1)</sup>
e.g.
@@ -737,7 +743,7 @@ Security Options
- **label=disable**: Turn off label separation for the container
- **no-new-privileges**: Disable container processes from gaining additional privileges
- **seccomp=unconfined**: Turn off seccomp confinement for the container
-- **seccomp**=_profile.json_: Whitelisted syscalls seccomp JSON file to be used as a seccomp filter
+- **seccomp**=_profile.json_: Allowed syscall list seccomp JSON file to be used as a seccomp filter
Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
@@ -903,7 +909,7 @@ container. Similarly, _source-volume_:_/container-dir_ will mount the volume
in the host to the container. If no such named volume exists, Podman will
create one.
-The _options_ is a comma delimited list and can be:
+The _options_ is a comma delimited list and can be: <sup>[[1]](#Footnote1)</sup>
* **rw**|**ro**
* **z**|**Z**
@@ -953,7 +959,7 @@ this behavior by specifying a volume mount propagation property. Making a
volume shared mounts done under that volume inside container will be
visible on host and vice versa. Making a volume **slave** enables only one
way mount propagation and that is mounts done on host under that volume
-will be visible inside container but not the other way around.
+will be visible inside container but not the other way around. <sup>[[1]](#Footnote1)</sup>
To control mount propagation property of volume one can use [**r**]**shared**,
[**r**]**slave** or [**r**]**private** propagation flag. Propagation property can
@@ -961,7 +967,7 @@ be specified only for bind mounted volumes and not for internal volumes or
named volumes. For mount propagation to work source mount point (mount point
where source dir is mounted on) has to have right propagation properties. For
shared volumes, source mount point has to be shared. And for slave volumes,
-source mount has to be either shared or slave.
+source mount has to be either shared or slave. <sup>[[1]](#Footnote1)</sup>
If you want to recursively mount a volume and all of its submounts into a
container, then you can use the **rbind** option. By default the bind option is
@@ -988,7 +994,7 @@ properties of source mount. If **findmnt**(1) utility is not available, then one
can look at mount entry for source mount point in _/proc/self/mountinfo_. Look
at the "optional fields" and see if any propagation properties are specified.
In there, **shared:N** means the mount is shared, **master:N** means mount
-is slave, and if nothing is there, the mount is private.
+is slave, and if nothing is there, the mount is private. <sup>[[1]](#Footnote1)</sup>
To change propagation properties of a mount point, use **mount**(8) command. For
example, if one wants to bind mount source directory _/foo_, one can do
@@ -1371,3 +1377,6 @@ July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
April 2014, Originally compiled by William Henry <whenry@redhat.com> based on docker.com source material and internal work.
+
+## FOOTNOTES
+<a name="Footnote1">1</a>: The Podman project is committed to inclusivity, a core value of open source. The `master` and `slave` mount propagation terminology used here is problematic and divisive, and should be changed. However, these terms are currently used within the Linux kernel and must be used as-is at this time. When the kernel maintainers rectify this usage, Podman will follow suit immediately.
diff --git a/libpod/container_api.go b/libpod/container_api.go
index b37b05ff2..487f75e67 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -29,7 +29,7 @@ import (
// Init requires that all dependency containers be started (e.g. pod infra
// containers). The `recursive` parameter will, if set to true, start these
// dependency containers before initializing this container.
-func (c *Container) Init(ctx context.Context, recursive bool) (err error) {
+func (c *Container) Init(ctx context.Context, recursive bool) error {
span, _ := opentracing.StartSpanFromContext(ctx, "containerInit")
span.SetTag("struct", "container")
defer span.Finish()
@@ -85,7 +85,7 @@ func (c *Container) Init(ctx context.Context, recursive bool) (err error) {
// Start requites that all dependency containers (e.g. pod infra containers) be
// running before being run. The recursive parameter, if set, will start all
// dependencies before starting this container.
-func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
+func (c *Container) Start(ctx context.Context, recursive bool) error {
span, _ := opentracing.StartSpanFromContext(ctx, "containerStart")
span.SetTag("struct", "container")
defer span.Finish()
@@ -112,7 +112,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
// Attach call occurs before Start).
// In overall functionality, it is identical to the Start call, with the added
// side effect that an attach session will also be started.
-func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (attachResChan <-chan error, err error) {
+func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachStreams, keys string, resize <-chan remotecommand.TerminalSize, recursive bool) (<-chan error, error) {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -150,7 +150,7 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *define.AttachSt
}
// RestartWithTimeout restarts a running container and takes a given timeout in uint
-func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err error) {
+func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) error {
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@@ -160,7 +160,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e
}
}
- if err = c.checkDependenciesAndHandleError(); err != nil {
+ if err := c.checkDependenciesAndHandleError(); err != nil {
return err
}
@@ -353,7 +353,7 @@ func (c *Container) HTTPAttach(httpCon net.Conn, httpBuf *bufio.ReadWriter, stre
logOpts.WaitGroup.Wait()
close(logChan)
}()
- if err := c.ReadLog(logOpts, logChan); err != nil {
+ if err := c.ReadLog(context.Background(), logOpts, logChan); err != nil {
return err
}
logrus.Debugf("Done reading logs for container %s, %d bytes", c.ID(), logSize)
@@ -760,7 +760,7 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
}
// Restore restores a container
-func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
+func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) error {
logrus.Debugf("Trying to restore container %s", c.ID())
if !c.batched {
c.lock.Lock()
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index 69da6fcfe..bd04ee9b9 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -779,25 +779,25 @@ func (c *Container) execOCILog(sessionID string) string {
}
// create a bundle path and associated files for an exec session
-func (c *Container) createExecBundle(sessionID string) (err error) {
+func (c *Container) createExecBundle(sessionID string) (retErr error) {
bundlePath := c.execBundlePath(sessionID)
- if createErr := os.MkdirAll(bundlePath, execDirPermission); createErr != nil {
- return createErr
+ if err := os.MkdirAll(bundlePath, execDirPermission); err != nil {
+ return err
}
defer func() {
- if err != nil {
- if err2 := os.RemoveAll(bundlePath); err != nil {
- logrus.Warnf("error removing exec bundle after creation caused another error: %v", err2)
+ if retErr != nil {
+ if err := os.RemoveAll(bundlePath); err != nil {
+ logrus.Warnf("error removing exec bundle after creation caused another error: %v", err)
}
}
}()
- if err2 := os.MkdirAll(c.execExitFileDir(sessionID), execDirPermission); err2 != nil {
+ if err := os.MkdirAll(c.execExitFileDir(sessionID), execDirPermission); err != nil {
// The directory is allowed to exist
- if !os.IsExist(err2) {
- err = errors.Wrapf(err2, "error creating OCI runtime exit file path %s", c.execExitFileDir(sessionID))
+ if !os.IsExist(err) {
+ return errors.Wrapf(err, "error creating OCI runtime exit file path %s", c.execExitFileDir(sessionID))
}
}
- return
+ return nil
}
// readExecExitCode reads the exit file for an exec session and returns
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 03684ddec..cd2e41be2 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -289,6 +289,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.OpenStdin = c.config.Stdin
ctrConfig.Image = c.config.RootfsImageName
+ ctrConfig.SystemdMode = c.config.Systemd
// Leave empty is not explicitly overwritten by user
if len(c.config.Command) != 0 {
@@ -610,22 +611,11 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
// Port bindings.
// Only populate if we're using CNI to configure the network.
- portBindings := make(map[string][]define.InspectHostPort)
if c.config.CreateNetNS {
- for _, port := range c.config.PortMappings {
- key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
- hostPorts := portBindings[key]
- if hostPorts == nil {
- hostPorts = []define.InspectHostPort{}
- }
- hostPorts = append(hostPorts, define.InspectHostPort{
- HostIP: port.HostIP,
- HostPort: fmt.Sprintf("%d", port.HostPort),
- })
- portBindings[key] = hostPorts
- }
+ hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings)
+ } else {
+ hostConfig.PortBindings = make(map[string][]define.InspectHostPort)
}
- hostConfig.PortBindings = portBindings
// Cap add and cap drop.
// We need a default set of capabilities to compare against.
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 7a547e565..c44ba5fe6 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -209,7 +209,7 @@ func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error {
// Handle container restart policy.
// This is called when a container has exited, and was not explicitly stopped by
// an API call to stop the container or pod it is in.
-func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, err error) {
+func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr error) {
// If we did not get a restart policy match, exit immediately.
// Do the same if we're not a policy that restarts.
if !c.state.RestartPolicyMatch ||
@@ -240,7 +240,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
logrus.Debugf("Restarting container %s due to restart policy %s", c.ID(), c.config.RestartPolicy)
// Need to check if dependencies are alive.
- if err = c.checkDependenciesAndHandleError(); err != nil {
+ if err := c.checkDependenciesAndHandleError(); err != nil {
return false, err
}
@@ -262,9 +262,9 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
}
defer func() {
- if err != nil {
- if err2 := c.cleanup(ctx); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ if retErr != nil {
+ if err := c.cleanup(ctx); err != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
}
}
}()
@@ -767,7 +767,7 @@ func (c *Container) save() error {
// Checks the container is in the right state, then initializes the container in preparation to start the container.
// If recursive is true, each of the containers dependencies will be started.
// Otherwise, this function will return with error if there are dependencies of this container that aren't running.
-func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err error) {
+func (c *Container) prepareToStart(ctx context.Context, recursive bool) (retErr error) {
// Container must be created or stopped to be started
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
@@ -784,9 +784,9 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err
}
defer func() {
- if err != nil {
- if err2 := c.cleanup(ctx); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ if retErr != nil {
+ if err := c.cleanup(ctx); err != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
}
}
}()
@@ -1132,7 +1132,7 @@ func (c *Container) reinit(ctx context.Context, retainRetries bool) error {
// Initialize (if necessary) and start a container
// Performs all necessary steps to start a container that is not running
// Does not lock or check validity
-func (c *Container) initAndStart(ctx context.Context) (err error) {
+func (c *Container) initAndStart(ctx context.Context) (retErr error) {
// If we are ContainerStateUnknown, throw an error
if c.state.State == define.ContainerStateUnknown {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in an unknown state", c.ID())
@@ -1150,9 +1150,9 @@ func (c *Container) initAndStart(ctx context.Context) (err error) {
}
defer func() {
- if err != nil {
- if err2 := c.cleanup(ctx); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ if retErr != nil {
+ if err := c.cleanup(ctx); err != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
}
}
}()
@@ -1321,7 +1321,7 @@ func (c *Container) unpause() error {
}
// Internal, non-locking function to restart a container
-func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err error) {
+func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (retErr error) {
if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
}
@@ -1358,9 +1358,9 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
}
}
defer func() {
- if err != nil {
- if err2 := c.cleanup(ctx); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ if retErr != nil {
+ if err := c.cleanup(ctx); err != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
}
}
}()
@@ -1682,7 +1682,7 @@ func (c *Container) cleanup(ctx context.Context) error {
// delete deletes the container and runs any configured poststop
// hooks.
-func (c *Container) delete(ctx context.Context) (err error) {
+func (c *Container) delete(ctx context.Context) error {
span, _ := opentracing.StartSpanFromContext(ctx, "delete")
span.SetTag("struct", "container")
defer span.Finish()
@@ -1701,7 +1701,7 @@ func (c *Container) delete(ctx context.Context) (err error) {
// postDeleteHooks runs the poststop hooks (if any) as specified by
// the OCI Runtime Specification (which requires them to run
// post-delete, despite the stage name).
-func (c *Container) postDeleteHooks(ctx context.Context) (err error) {
+func (c *Container) postDeleteHooks(ctx context.Context) error {
span, _ := opentracing.StartSpanFromContext(ctx, "postDeleteHooks")
span.SetTag("struct", "container")
defer span.Finish()
@@ -1824,7 +1824,7 @@ func (c *Container) saveSpec(spec *spec.Spec) error {
}
// Warning: precreate hooks may alter 'config' in place.
-func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) {
+func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (map[string][]spec.Hook, error) {
allHooks := make(map[string][]spec.Hook)
if c.runtime.config.Engine.HooksDir == nil {
if rootless.IsRootless() {
@@ -1938,7 +1938,7 @@ func (c *Container) checkReadyForRemoval() error {
// writeJSONFile marshalls and writes the given data to a JSON file
// in the bundle path
-func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
+func (c *Container) writeJSONFile(v interface{}, file string) error {
fileJSON, err := json.MarshalIndent(v, "", " ")
if err != nil {
return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID())
@@ -1953,7 +1953,7 @@ func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
// prepareCheckpointExport writes the config and spec to
// JSON files for later export
-func (c *Container) prepareCheckpointExport() (err error) {
+func (c *Container) prepareCheckpointExport() error {
// save live config
if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil {
return err
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index cb70aea62..cfcf9b823 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -244,7 +244,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
// Apply AppArmor checks and load the default profile if needed.
- if !c.config.Privileged {
+ if len(c.config.Spec.Process.ApparmorProfile) > 0 {
updatedProfile, err := apparmor.CheckProfileAndLoadDefault(c.config.Spec.Process.ApparmorProfile)
if err != nil {
return nil, err
@@ -610,7 +610,7 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
return nil
}
-func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) {
+func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) error {
if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
}
@@ -721,7 +721,7 @@ func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error)
return nil
}
-func (c *Container) checkpointRestoreSupported() (err error) {
+func (c *Container) checkpointRestoreSupported() error {
if !criu.CheckForCriu() {
return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion)
}
@@ -731,7 +731,7 @@ func (c *Container) checkpointRestoreSupported() (err error) {
return nil
}
-func (c *Container) checkpointRestoreLabelLog(fileName string) (err error) {
+func (c *Container) checkpointRestoreLabelLog(fileName string) error {
// Create the CRIU log file and label it
dumpLog := filepath.Join(c.bundlePath(), fileName)
@@ -748,7 +748,7 @@ func (c *Container) checkpointRestoreLabelLog(fileName string) (err error) {
return nil
}
-func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) (err error) {
+func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
if err := c.checkpointRestoreSupported(); err != nil {
return err
}
@@ -818,7 +818,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
return c.save()
}
-func (c *Container) importCheckpoint(input string) (err error) {
+func (c *Container) importCheckpoint(input string) error {
archiveFile, err := os.Open(input)
if err != nil {
return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
@@ -847,8 +847,7 @@ func (c *Container) importCheckpoint(input string) (err error) {
return nil
}
-func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
-
+func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) {
if err := c.checkpointRestoreSupported(); err != nil {
return err
}
@@ -858,7 +857,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
if options.TargetFile != "" {
- if err = c.importCheckpoint(options.TargetFile); err != nil {
+ if err := c.importCheckpoint(options.TargetFile); err != nil {
return err
}
}
@@ -944,9 +943,9 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
defer func() {
- if err != nil {
- if err2 := c.cleanup(ctx); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ if retErr != nil {
+ if err := c.cleanup(ctx); err != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err)
}
}
}()
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index a42c1d735..e6d94104c 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -18,7 +18,7 @@ func (c *Container) unmountSHM(mount string) error {
return define.ErrNotImplemented
}
-func (c *Container) prepare() (err error) {
+func (c *Container) prepare() error {
return define.ErrNotImplemented
}
diff --git a/libpod/container_log.go b/libpod/container_log.go
index 97936c683..80f8e6e50 100644
--- a/libpod/container_log.go
+++ b/libpod/container_log.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "context"
"fmt"
"os"
"time"
@@ -13,9 +14,9 @@ import (
)
// Log is a runtime function that can read one or more container logs.
-func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
for _, ctr := range containers {
- if err := ctr.ReadLog(options, logChannel); err != nil {
+ if err := ctr.ReadLog(ctx, options, logChannel); err != nil {
return err
}
}
@@ -23,25 +24,25 @@ func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChan
}
// ReadLog reads a containers log based on the input options and returns loglines over a channel.
-func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) ReadLog(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
switch c.LogDriver() {
case define.NoLogging:
return errors.Wrapf(define.ErrNoLogs, "this container is using the 'none' log driver, cannot read logs")
case define.JournaldLogging:
// TODO Skip sending logs until journald logs can be read
- return c.readFromJournal(options, logChannel)
+ return c.readFromJournal(ctx, options, logChannel)
case define.JSONLogging:
// TODO provide a separate implementation of this when Conmon
// has support.
fallthrough
case define.KubernetesLogging, "":
- return c.readFromLogFile(options, logChannel)
+ return c.readFromLogFile(ctx, options, logChannel)
default:
return errors.Wrapf(define.ErrInternal, "unrecognized log driver %q, cannot read logs", c.LogDriver())
}
}
-func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromLogFile(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
t, tailLog, err := logs.GetLogFile(c.LogPath(), options)
if err != nil {
// If the log file does not exist, this is not fatal.
@@ -62,8 +63,17 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l
}
go func() {
+ defer options.WaitGroup.Done()
+
var partial string
for line := range t.Lines {
+ select {
+ case <-ctx.Done():
+ // the consumer has cancelled
+ return
+ default:
+ // fallthrough
+ }
nll, err := logs.NewLogLine(line.Text)
if err != nil {
logrus.Error(err)
@@ -82,7 +92,6 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l
logChannel <- nll
}
}
- options.WaitGroup.Done()
}()
// Check if container is still running or paused
if options.Follow {
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index fad3bf87c..00b2039a9 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -4,6 +4,7 @@
package libpod
import (
+ "context"
"fmt"
"io"
"math"
@@ -29,7 +30,7 @@ const (
bufLen = 16384
)
-func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error {
var config journal.JournalReaderConfig
if options.Tail < 0 {
config.NumFromTail = math.MaxUint64
@@ -65,13 +66,24 @@ func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *l
if options.Follow {
go func() {
+ done := make(chan bool)
+ until := make(chan time.Time)
+ go func() {
+ select {
+ case <-ctx.Done():
+ until <- time.Time{}
+ case <-done:
+ // nothing to do anymore
+ }
+ }()
follower := FollowBuffer{logChannel}
- err := r.Follow(nil, follower)
+ err := r.Follow(until, follower)
if err != nil {
logrus.Debugf(err.Error())
}
r.Close()
options.WaitGroup.Done()
+ done <- true
return
}()
return nil
diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go
index 18882720a..f3b36619e 100644
--- a/libpod/container_log_unsupported.go
+++ b/libpod/container_log_unsupported.go
@@ -3,11 +3,13 @@
package libpod
import (
+ "context"
+
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/libpod/logs"
"github.com/pkg/errors"
)
-func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
+func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine) error {
return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux")
}
diff --git a/libpod/define/config.go b/libpod/define/config.go
index 900a363d8..64b24d9e2 100644
--- a/libpod/define/config.go
+++ b/libpod/define/config.go
@@ -75,3 +75,13 @@ const JSONLogging = "json-file"
// NoLogging is the string conmon expects when specifying to use no log driver whatsoever
const NoLogging = "none"
+
+// Strings used for --sdnotify option to podman
+const (
+ SdNotifyModeContainer = "container"
+ SdNotifyModeConmon = "conmon"
+ SdNotifyModeIgnore = "ignore"
+)
+
+// DefaultRlimitValue is the value set by default for nofile and nproc
+const RLimitDefaultValue = uint64(1048576)
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index a3cf4304f..7807f9e8b 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -54,6 +54,10 @@ type InspectContainerConfig struct {
// CreateCommand is the full command plus arguments of the process the
// container has been created with.
CreateCommand []string `json:"CreateCommand,omitempty"`
+ // SystemdMode is whether the container is running in systemd mode. In
+ // systemd mode, the container configuration is customized to optimize
+ // running systemd in the container.
+ SystemdMode bool `json:"SystemdMode,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
@@ -628,3 +632,56 @@ type InspectContainerData struct {
Config *InspectContainerConfig `json:"Config"`
HostConfig *InspectContainerHostConfig `json:"HostConfig"`
}
+
+// InspectExecSession contains information about a given exec session.
+type InspectExecSession struct {
+ // CanRemove is legacy and used purely for compatibility reasons.
+ // Will always be set to true, unless the exec session is running.
+ CanRemove bool `json:"CanRemove"`
+ // ContainerID is the ID of the container this exec session is attached
+ // to.
+ ContainerID string `json:"ContainerID"`
+ // DetachKeys are the detach keys used by the exec session.
+ // If set to "" the default keys are being used.
+ // Will show "<none>" if no detach keys are set.
+ DetachKeys string `json:"DetachKeys"`
+ // ExitCode is the exit code of the exec session. Will be set to 0 if
+ // the exec session has not yet exited.
+ ExitCode int `json:"ExitCode"`
+ // ID is the ID of the exec session.
+ ID string `json:"ID"`
+ // OpenStderr is whether the container's STDERR stream will be attached.
+ // Always set to true if the exec session created a TTY.
+ OpenStderr bool `json:"OpenStderr"`
+ // OpenStdin is whether the container's STDIN stream will be attached
+ // to.
+ OpenStdin bool `json:"OpenStdin"`
+ // OpenStdout is whether the container's STDOUT stream will be attached.
+ // Always set to true if the exec session created a TTY.
+ OpenStdout bool `json:"OpenStdout"`
+ // Running is whether the exec session is running.
+ Running bool `json:"Running"`
+ // Pid is the PID of the exec session's process.
+ // Will be set to 0 if the exec session is not running.
+ Pid int `json:"Pid"`
+ // ProcessConfig contains information about the exec session's process.
+ ProcessConfig *InspectExecProcess `json:"ProcessConfig"`
+}
+
+// InspectExecProcess contains information about the process in a given exec
+// session.
+type InspectExecProcess struct {
+ // Arguments are the arguments to the entrypoint command of the exec
+ // session.
+ Arguments []string `json:"arguments"`
+ // Entrypoint is the entrypoint for the exec session (the command that
+ // will be executed in the container).
+ Entrypoint string `json:"entrypoint"`
+ // Privileged is whether the exec session will be started with elevated
+ // privileges.
+ Privileged bool `json:"privileged"`
+ // Tty is whether the exec session created a terminal.
+ Tty bool `json:"tty"`
+ // User is the user the exec session was started as.
+ User string `json:"user"`
+}
diff --git a/libpod/define/ctr_inspect.go b/libpod/define/ctr_inspect.go
deleted file mode 100644
index b7cd13f82..000000000
--- a/libpod/define/ctr_inspect.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package define
-
-// InspectExecSession contains information about a given exec session.
-type InspectExecSession struct {
- // CanRemove is legacy and used purely for compatibility reasons.
- // Will always be set to true, unless the exec session is running.
- CanRemove bool `json:"CanRemove"`
- // ContainerID is the ID of the container this exec session is attached
- // to.
- ContainerID string `json:"ContainerID"`
- // DetachKeys are the detach keys used by the exec session.
- // If set to "" the default keys are being used.
- // Will show "<none>" if no detach keys are set.
- DetachKeys string `json:"DetachKeys"`
- // ExitCode is the exit code of the exec session. Will be set to 0 if
- // the exec session has not yet exited.
- ExitCode int `json:"ExitCode"`
- // ID is the ID of the exec session.
- ID string `json:"ID"`
- // OpenStderr is whether the container's STDERR stream will be attached.
- // Always set to true if the exec session created a TTY.
- OpenStderr bool `json:"OpenStderr"`
- // OpenStdin is whether the container's STDIN stream will be attached
- // to.
- OpenStdin bool `json:"OpenStdin"`
- // OpenStdout is whether the container's STDOUT stream will be attached.
- // Always set to true if the exec session created a TTY.
- OpenStdout bool `json:"OpenStdout"`
- // Running is whether the exec session is running.
- Running bool `json:"Running"`
- // Pid is the PID of the exec session's process.
- // Will be set to 0 if the exec session is not running.
- Pid int `json:"Pid"`
- // ProcessConfig contains information about the exec session's process.
- ProcessConfig *InspectExecProcess `json:"ProcessConfig"`
-}
-
-// InspectExecProcess contains information about the process in a given exec
-// session.
-type InspectExecProcess struct {
- // Arguments are the arguments to the entrypoint command of the exec
- // session.
- Arguments []string `json:"arguments"`
- // Entrypoint is the entrypoint for the exec session (the command that
- // will be executed in the container).
- Entrypoint string `json:"entrypoint"`
- // Privileged is whether the exec session will be started with elevated
- // privileges.
- Privileged bool `json:"privileged"`
- // Tty is whether the exec session created a terminal.
- Tty bool `json:"tty"`
- // User is the user the exec session was started as.
- User string `json:"user"`
-}
diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go
index 7f06e16fc..634cbb728 100644
--- a/libpod/define/pod_inspect.go
+++ b/libpod/define/pod_inspect.go
@@ -3,8 +3,6 @@ package define
import (
"net"
"time"
-
- "github.com/cri-o/ocicni/pkg/ocicni"
)
// InspectPodData contains detailed information on a pod's configuration and
@@ -60,7 +58,7 @@ type InspectPodData struct {
type InspectPodInfraConfig struct {
// PortBindings are ports that will be forwarded to the infra container
// and then shared with the pod.
- PortBindings []ocicni.PortMapping
+ PortBindings map[string][]InspectHostPort
// HostNetwork is whether the infra container (and thus the whole pod)
// will use the host's network and not create a network namespace.
HostNetwork bool
@@ -89,6 +87,8 @@ type InspectPodInfraConfig struct {
// HostAdd adds a number of hosts to the infra container's resolv.conf
// which will be shared with the rest of the pod.
HostAdd []string
+ // Networks is a list of CNI networks te pod will join.
+ Networks []string
}
// InspectPodContainerInfo contains information on a container in a pod.
diff --git a/libpod/events/events_linux.go b/libpod/events/events_linux.go
index ffb100be8..482d7d6dd 100644
--- a/libpod/events/events_linux.go
+++ b/libpod/events/events_linux.go
@@ -8,20 +8,20 @@ import (
)
// NewEventer creates an eventer based on the eventer type
-func NewEventer(options EventerOptions) (eventer Eventer, err error) {
+func NewEventer(options EventerOptions) (Eventer, error) {
logrus.Debugf("Initializing event backend %s", options.EventerType)
switch strings.ToUpper(options.EventerType) {
case strings.ToUpper(Journald.String()):
- eventer, err = newEventJournalD(options)
+ eventer, err := newEventJournalD(options)
if err != nil {
return nil, errors.Wrapf(err, "eventer creation")
}
+ return eventer, nil
case strings.ToUpper(LogFile.String()):
- eventer = EventLogFile{options}
+ return EventLogFile{options}, nil
case strings.ToUpper(Null.String()):
- eventer = NewNullEventer()
+ return NewNullEventer(), nil
default:
return nil, errors.Errorf("unknown event logger type: %s", strings.ToUpper(options.EventerType))
}
- return eventer, nil
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 7985e17e1..1e79e8732 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -141,18 +141,18 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
}
// Create and configure a new network namespace for a container
-func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, err error) {
+func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result, retErr error) {
ctrNS, err := netns.NewNS()
if err != nil {
return nil, nil, errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
}
defer func() {
- if err != nil {
- if err2 := netns.UnmountNS(ctrNS); err2 != nil {
- logrus.Errorf("Error unmounting partially created network namespace for container %s: %v", ctr.ID(), err2)
+ if retErr != nil {
+ if err := netns.UnmountNS(ctrNS); err != nil {
+ logrus.Errorf("Error unmounting partially created network namespace for container %s: %v", ctr.ID(), err)
}
- if err2 := ctrNS.Close(); err2 != nil {
- logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err2)
+ if err := ctrNS.Close(); err != nil {
+ logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err)
}
}
}()
@@ -188,7 +188,7 @@ func checkSlirpFlags(path string) (*slirpFeatures, error) {
}
// Configure the network namespace for a rootless container
-func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
+func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
path := r.config.Engine.NetworkCmdPath
if path == "" {
@@ -342,7 +342,7 @@ func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout t
return nil
}
-func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) (err error) {
+func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) error {
syncR, syncW, err := os.Pipe()
if err != nil {
return errors.Wrapf(err, "failed to open pipe")
@@ -420,7 +420,7 @@ func (r *Runtime) setupRootlessPortMapping(ctr *Container, netnsPath string) (er
}
// Configure the network namespace using the container process
-func (r *Runtime) setupNetNS(ctr *Container) (err error) {
+func (r *Runtime) setupNetNS(ctr *Container) error {
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
b := make([]byte, 16)
@@ -587,21 +587,7 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
// network.
func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) {
settings := new(define.InspectNetworkSettings)
- settings.Ports = make(map[string][]define.InspectHostPort)
- if c.config.PortMappings != nil {
- for _, port := range c.config.PortMappings {
- key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
- mapping := settings.Ports[key]
- if mapping == nil {
- mapping = []define.InspectHostPort{}
- }
- mapping = append(mapping, define.InspectHostPort{
- HostIP: port.HostIP,
- HostPort: fmt.Sprintf("%d", port.HostPort),
- })
- settings.Ports[key] = mapping
- }
- }
+ settings.Ports = makeInspectPortBindings(c.config.PortMappings)
// We can't do more if the network is down.
if c.state.NetNS == nil {
diff --git a/libpod/networking_unsupported.go b/libpod/networking_unsupported.go
index d5189709c..69f470ff7 100644
--- a/libpod/networking_unsupported.go
+++ b/libpod/networking_unsupported.go
@@ -4,11 +4,11 @@ package libpod
import "github.com/containers/libpod/v2/libpod/define"
-func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
+func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
return define.ErrNotImplemented
}
-func (r *Runtime) setupNetNS(ctr *Container) (err error) {
+func (r *Runtime) setupNetNS(ctr *Container) error {
return define.ErrNotImplemented
}
@@ -16,7 +16,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
return define.ErrNotImplemented
}
-func (r *Runtime) createNetNS(ctr *Container) (err error) {
+func (r *Runtime) createNetNS(ctr *Container) error {
return define.ErrNotImplemented
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index e9d5cbaa3..e38646eec 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -190,7 +190,7 @@ func hasCurrentUserMapped(ctr *Container) bool {
}
// CreateContainer creates a container.
-func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
+func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
if !hasCurrentUserMapped(ctr) {
for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.Engine.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.Engine.VolumePath} {
if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil {
@@ -850,7 +850,7 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
}
// createOCIContainer generates this container's main conmon instance and prepares it for starting
-func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) {
+func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
var stderrBuf bytes.Buffer
runtimeDir, err := util.GetRuntimeDir()
@@ -1297,8 +1297,9 @@ func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec
return nil
}
-// newPipe creates a unix socket pair for communication
-func newPipe() (parent *os.File, child *os.File, err error) {
+// newPipe creates a unix socket pair for communication.
+// Returns two files - first is parent, second is child.
+func newPipe() (*os.File, *os.File, error) {
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
if err != nil {
return nil, nil, err
diff --git a/libpod/options.go b/libpod/options.go
index 3120a35d7..bff3f3c18 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1371,12 +1371,12 @@ func WithHealthCheck(healthCheck *manifest.Schema2HealthConfig) CtrCreateOption
// WithCreateCommand adds the full command plus arguments of the current
// process to the container config.
-func WithCreateCommand() CtrCreateOption {
+func WithCreateCommand(cmd []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
- ctr.config.CreateCommand = os.Args
+ ctr.config.CreateCommand = cmd
return nil
}
}
@@ -1553,12 +1553,12 @@ func WithPodHostname(hostname string) PodCreateOption {
// WithPodCreateCommand adds the full command plus arguments of the current
// process to the pod config.
-func WithPodCreateCommand() PodCreateOption {
+func WithPodCreateCommand(createCmd []string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return define.ErrPodFinalized
}
- pod.config.CreateCommand = os.Args
+ pod.config.CreateCommand = createCmd
return nil
}
}
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index a02b171e1..f2ef81bec 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -481,6 +481,41 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
}
}
+ // Infra config contains detailed information on the pod's infra
+ // container.
+ var infraConfig *define.InspectPodInfraConfig
+ if p.config.InfraContainer != nil && p.config.InfraContainer.HasInfraContainer {
+ infraConfig = new(define.InspectPodInfraConfig)
+ infraConfig.HostNetwork = p.config.InfraContainer.HostNetwork
+ infraConfig.StaticIP = p.config.InfraContainer.StaticIP
+ infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC
+ infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf
+ infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts
+
+ if len(p.config.InfraContainer.DNSServer) > 0 {
+ infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer))
+ infraConfig.DNSServer = append(infraConfig.DNSServer, p.config.InfraContainer.DNSServer...)
+ }
+ if len(p.config.InfraContainer.DNSSearch) > 0 {
+ infraConfig.DNSSearch = make([]string, 0, len(p.config.InfraContainer.DNSSearch))
+ infraConfig.DNSSearch = append(infraConfig.DNSSearch, p.config.InfraContainer.DNSSearch...)
+ }
+ if len(p.config.InfraContainer.DNSOption) > 0 {
+ infraConfig.DNSOption = make([]string, 0, len(p.config.InfraContainer.DNSOption))
+ infraConfig.DNSOption = append(infraConfig.DNSOption, p.config.InfraContainer.DNSOption...)
+ }
+ if len(p.config.InfraContainer.HostAdd) > 0 {
+ infraConfig.HostAdd = make([]string, 0, len(p.config.InfraContainer.HostAdd))
+ infraConfig.HostAdd = append(infraConfig.HostAdd, p.config.InfraContainer.HostAdd...)
+ }
+ if len(p.config.InfraContainer.Networks) > 0 {
+ infraConfig.Networks = make([]string, 0, len(p.config.InfraContainer.Networks))
+ infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...)
+ }
+
+ infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings)
+ }
+
inspectData := define.InspectPodData{
ID: p.ID(),
Name: p.Name(),
@@ -490,12 +525,12 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
State: podState,
Hostname: p.config.Hostname,
Labels: p.Labels(),
- CreateCgroup: false,
+ CreateCgroup: p.config.UsePodCgroup,
CgroupParent: p.CgroupParent(),
CgroupPath: p.state.CgroupPath,
- CreateInfra: false,
+ CreateInfra: infraConfig != nil,
InfraContainerID: p.state.InfraContainerID,
- InfraConfig: nil,
+ InfraConfig: infraConfig,
SharedNamespaces: sharesNS,
NumContainers: uint(len(containers)),
Containers: ctrs,
diff --git a/libpod/runtime.go b/libpod/runtime.go
index bf1e203c1..24370d50e 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -126,7 +126,7 @@ func SetXdgDirs() error {
// NewRuntime creates a new container runtime
// Options can be passed to override the default configuration for the runtime
-func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime, err error) {
+func NewRuntime(ctx context.Context, options ...RuntimeOption) (*Runtime, error) {
conf, err := config.NewConfig("")
if err != nil {
return nil, err
@@ -140,13 +140,13 @@ func NewRuntime(ctx context.Context, options ...RuntimeOption) (runtime *Runtime
// functions can be used to mutate this configuration further.
// An error will be returned if the configuration file at the given path does
// not exist or cannot be loaded
-func NewRuntimeFromConfig(ctx context.Context, userConfig *config.Config, options ...RuntimeOption) (runtime *Runtime, err error) {
+func NewRuntimeFromConfig(ctx context.Context, userConfig *config.Config, options ...RuntimeOption) (*Runtime, error) {
return newRuntimeFromConfig(ctx, userConfig, options...)
}
-func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...RuntimeOption) (runtime *Runtime, err error) {
- runtime = new(Runtime)
+func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...RuntimeOption) (*Runtime, error) {
+ runtime := new(Runtime)
if conf.Engine.OCIRuntime == "" {
conf.Engine.OCIRuntime = "runc"
@@ -236,7 +236,7 @@ func getLockManager(runtime *Runtime) (lock.Manager, error) {
// Make a new runtime based on the given configuration
// Sets up containers/storage, state store, OCI runtime
-func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
+func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
// Find a working conmon binary
cPath, err := runtime.config.FindConmon()
if err != nil {
@@ -316,12 +316,11 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) {
return err
}
defer func() {
- if err != nil && store != nil {
+ if retErr != nil && store != nil {
// Don't forcibly shut down
// We could be opening a store in use by another libpod
- _, err2 := store.Shutdown(false)
- if err2 != nil {
- logrus.Errorf("Error removing store for partially-created runtime: %s", err2)
+ if _, err := store.Shutdown(false); err != nil {
+ logrus.Errorf("Error removing store for partially-created runtime: %s", err)
}
}
}()
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index c073aabb5..e2bb696b0 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -34,7 +34,7 @@ type CtrCreateOption func(*Container) error
type ContainerFilter func(*Container) bool
// NewContainer creates a new container from a given OCI config.
-func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
+func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
@@ -44,7 +44,7 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ..
}
// RestoreContainer re-creates a container from an imported checkpoint
-func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
+func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (*Container, error) {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
@@ -68,7 +68,7 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config
return r.setupContainer(ctx, ctr)
}
-func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
+func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (*Container, error) {
if rSpec == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "must provide a valid runtime spec to create container")
}
@@ -122,7 +122,7 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf
return ctr, nil
}
-func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
+func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "newContainer")
span.SetTag("type", "runtime")
defer span.Finish()
@@ -141,7 +141,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
return r.setupContainer(ctx, ctr)
}
-func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, err error) {
+func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Container, retErr error) {
// Validate the container
if err := ctr.validate(); err != nil {
return nil, err
@@ -157,9 +157,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
logrus.Debugf("Allocated lock %d for container %s", ctr.lock.ID(), ctr.ID())
defer func() {
- if err != nil {
- if err2 := ctr.lock.Free(); err2 != nil {
- logrus.Errorf("Error freeing lock for container after creation failed: %v", err2)
+ if retErr != nil {
+ if err := ctr.lock.Free(); err != nil {
+ logrus.Errorf("Error freeing lock for container after creation failed: %v", err)
}
}
}()
@@ -272,9 +272,9 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, err
}
defer func() {
- if err != nil {
- if err2 := ctr.teardownStorage(); err2 != nil {
- logrus.Errorf("Error removing partially-created container root filesystem: %s", err2)
+ if retErr != nil {
+ if err := ctr.teardownStorage(); err != nil {
+ logrus.Errorf("Error removing partially-created container root filesystem: %s", err)
}
}
}()
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 30ddf5e24..ef4f6f2be 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -34,13 +34,13 @@ var (
)
// Get an empty BoltDB state for use in tests
-func getEmptyBoltState() (s State, p string, m lock.Manager, err error) {
+func getEmptyBoltState() (_ State, _ string, _ lock.Manager, retErr error) {
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
if err != nil {
return nil, "", nil, err
}
defer func() {
- if err != nil {
+ if retErr != nil {
os.RemoveAll(tmpDir)
}
}()
@@ -66,13 +66,13 @@ func getEmptyBoltState() (s State, p string, m lock.Manager, err error) {
}
// Get an empty in-memory state for use in tests
-func getEmptyInMemoryState() (s State, p string, m lock.Manager, err error) {
+func getEmptyInMemoryState() (_ State, _ string, _ lock.Manager, retErr error) {
tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
if err != nil {
return nil, "", nil, err
}
defer func() {
- if err != nil {
+ if retErr != nil {
os.RemoveAll(tmpDir)
}
}()
diff --git a/libpod/storage.go b/libpod/storage.go
index be79b3fc0..e497d0daf 100644
--- a/libpod/storage.go
+++ b/libpod/storage.go
@@ -66,7 +66,7 @@ func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
// CreateContainerStorage creates the storage end of things. We already have the container spec created
// TO-DO We should be passing in an Image object in the future.
-func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (cinfo ContainerInfo, err error) {
+func (r *storageService) CreateContainerStorage(ctx context.Context, systemContext *types.SystemContext, imageName, imageID, containerName, containerID string, options storage.ContainerOptions) (_ ContainerInfo, retErr error) {
span, _ := opentracing.StartSpanFromContext(ctx, "createContainerStorage")
span.SetTag("type", "storageService")
defer span.Finish()
@@ -132,9 +132,9 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
// If anything fails after this point, we need to delete the incomplete
// container before returning.
defer func() {
- if err != nil {
- if err2 := r.store.DeleteContainer(container.ID); err2 != nil {
- logrus.Infof("%v deleting partially-created container %q", err2, container.ID)
+ if retErr != nil {
+ if err := r.store.DeleteContainer(container.ID); err != nil {
+ logrus.Infof("%v deleting partially-created container %q", err, container.ID)
return
}
diff --git a/libpod/util.go b/libpod/util.go
index 7504295f0..8c2d946ba 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -15,6 +15,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/utils"
+ "github.com/cri-o/ocicni/pkg/ocicni"
"github.com/fsnotify/fsnotify"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -254,3 +255,21 @@ func makeHTTPAttachHeader(stream byte, length uint32) []byte {
binary.BigEndian.PutUint32(header[4:], length)
return header
}
+
+// Convert OCICNI port bindings into Inspect-formatted port bindings.
+func makeInspectPortBindings(bindings []ocicni.PortMapping) map[string][]define.InspectHostPort {
+ portBindings := make(map[string][]define.InspectHostPort)
+ for _, port := range bindings {
+ key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
+ hostPorts := portBindings[key]
+ if hostPorts == nil {
+ hostPorts = []define.InspectHostPort{}
+ }
+ hostPorts = append(hostPorts, define.InspectHostPort{
+ HostIP: port.HostIP,
+ HostPort: fmt.Sprintf("%d", port.HostPort),
+ })
+ portBindings[key] = hostPorts
+ }
+ return portBindings
+}
diff --git a/pkg/api/handlers/compat/containers_attach.go b/pkg/api/handlers/compat/containers_attach.go
index 325f96b40..71586fca4 100644
--- a/pkg/api/handlers/compat/containers_attach.go
+++ b/pkg/api/handlers/compat/containers_attach.go
@@ -1,23 +1,22 @@
package compat
import (
+ "bufio"
"fmt"
+ "io"
+ "net"
"net/http"
+ "strings"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/libpod/define"
"github.com/containers/libpod/v2/pkg/api/handlers/utils"
+ "github.com/containers/libpod/v2/pkg/api/server/idletracker"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
-// AttachHeader is the literal header sent for upgraded/hijacked connections for
-// attach, sourced from Docker at:
-// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
-// Using literally to ensure compatibility with existing clients.
-const AttachHeader = "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n"
-
func AttachContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -98,21 +97,11 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
return
}
- // Hijack the connection
- hijacker, ok := w.(http.Hijacker)
- if !ok {
- utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
- return
- }
-
- connection, buffer, err := hijacker.Hijack()
+ connection, buffer, err := AttachConnection(w, r)
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ utils.InternalServerError(w, err)
return
}
-
- fmt.Fprintf(connection, AttachHeader)
-
logrus.Debugf("Hijack for attach of container %s successful", ctr.ID())
// Perform HTTP attach.
@@ -126,3 +115,57 @@ func AttachContainer(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("Attach for container %s completed successfully", ctr.ID())
}
+
+type HijackedConnection struct {
+ net.Conn // Connection
+ idleTracker *idletracker.IdleTracker // Connection tracker
+}
+
+func (c HijackedConnection) Close() error {
+ logrus.Debugf("Hijacked connection closed")
+
+ c.idleTracker.TrackHijackedClosed()
+ return c.Conn.Close()
+}
+
+func AttachConnection(w http.ResponseWriter, r *http.Request) (net.Conn, *bufio.ReadWriter, error) {
+ idleTracker := r.Context().Value("idletracker").(*idletracker.IdleTracker)
+
+ // Hijack the connection
+ hijacker, ok := w.(http.Hijacker)
+ if !ok {
+ return nil, nil, errors.Errorf("unable to hijack connection")
+ }
+
+ connection, buffer, err := hijacker.Hijack()
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error hijacking connection")
+ }
+ trackedConnection := HijackedConnection{
+ Conn: connection,
+ idleTracker: idleTracker,
+ }
+
+ WriteAttachHeaders(r, trackedConnection)
+
+ return trackedConnection, buffer, nil
+}
+
+func WriteAttachHeaders(r *http.Request, connection io.Writer) {
+ // AttachHeader is the literal header sent for upgraded/hijacked connections for
+ // attach, sourced from Docker at:
+ // https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
+ // Using literally to ensure compatibility with existing clients.
+ c := r.Header.Get("Connection")
+ proto := r.Header.Get("Upgrade")
+ if len(proto) == 0 || !strings.EqualFold(c, "Upgrade") {
+ // OK - can't upgrade if not requested or protocol is not specified
+ fmt.Fprintf(connection,
+ "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
+ } else {
+ // Upraded
+ fmt.Fprintf(connection,
+ "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: %s\r\n\r\n",
+ proto)
+ }
+}
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 8034a529c..cbee8a8b6 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -1,6 +1,7 @@
package compat
import (
+ "context"
"encoding/json"
"fmt"
"net/http"
@@ -40,6 +41,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
}
if len(input.HostConfig.Links) > 0 {
utils.Error(w, utils.ErrLinkNotSupport.Error(), http.StatusBadRequest, errors.Wrapf(utils.ErrLinkNotSupport, "bad parameter"))
+ return
}
newImage, err := runtime.ImageRuntime().NewFromLocal(input.Image)
if err != nil {
@@ -51,7 +53,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "GetConfig()"))
return
}
- cc, err := makeCreateConfig(containerConfig, input, newImage)
+ cc, err := makeCreateConfig(r.Context(), containerConfig, input, newImage)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "makeCreatConfig()"))
return
@@ -60,7 +62,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.CreateContainer(r.Context(), w, runtime, &cc)
}
-func makeCreateConfig(containerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
+func makeCreateConfig(ctx context.Context, containerConfig *config.Config, input handlers.CreateContainerConfig, newImage *image2.Image) (createconfig.CreateConfig, error) {
var (
err error
init bool
@@ -79,6 +81,22 @@ func makeCreateConfig(containerConfig *config.Config, input handlers.CreateConta
workDir = input.WorkingDir
}
+ if input.Entrypoint == nil {
+ entrypointSlice, err := newImage.Entrypoint(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ input.Entrypoint = entrypointSlice
+ }
+
+ if len(input.Cmd) == 0 {
+ cmdSlice, err := newImage.Cmd(ctx)
+ if err != nil {
+ return createconfig.CreateConfig{}, err
+ }
+ input.Cmd = cmdSlice
+ }
+
stopTimeout := containerConfig.Engine.StopTimeout
if input.StopTimeout != nil {
stopTimeout = uint(*input.StopTimeout)
@@ -217,5 +235,16 @@ func makeCreateConfig(containerConfig *config.Config, input handlers.CreateConta
Pid: pidConfig,
}
+
+ fullCmd := append(input.Entrypoint, input.Cmd...)
+ if len(fullCmd) > 0 {
+ m.PodmanPath = fullCmd[0]
+ if len(fullCmd) == 1 {
+ m.Args = fullCmd
+ } else {
+ m.Args = fullCmd[1:]
+ }
+ }
+
return m, nil
}
diff --git a/pkg/api/handlers/compat/containers_logs.go b/pkg/api/handlers/compat/containers_logs.go
index 8147f4d38..30ee030e8 100644
--- a/pkg/api/handlers/compat/containers_logs.go
+++ b/pkg/api/handlers/compat/containers_logs.go
@@ -92,7 +92,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
options.WaitGroup = &wg
logChannel := make(chan *logs.LogLine, tail+1)
- if err := runtime.Log([]*libpod.Container{ctnr}, options, logChannel); err != nil {
+ if err := runtime.Log(r.Context(), []*libpod.Container{ctnr}, options, logChannel); err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain logs for Container '%s'", name))
return
}
@@ -105,50 +105,48 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
var frame strings.Builder
header := make([]byte, 8)
- for ok := true; ok; ok = query.Follow {
- for line := range logChannel {
- if _, found := r.URL.Query()["until"]; found {
- if line.Time.After(until) {
- break
- }
+ for line := range logChannel {
+ if _, found := r.URL.Query()["until"]; found {
+ if line.Time.After(until) {
+ break
}
+ }
- // Reset buffer we're ready to loop again
- frame.Reset()
- switch line.Device {
- case "stdout":
- if !query.Stdout {
- continue
- }
- header[0] = 1
- case "stderr":
- if !query.Stderr {
- continue
- }
- header[0] = 2
- default:
- // Logging and moving on is the best we can do here. We may have already sent
- // a Status and Content-Type to client therefore we can no longer report an error.
- log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID())
+ // Reset buffer we're ready to loop again
+ frame.Reset()
+ switch line.Device {
+ case "stdout":
+ if !query.Stdout {
continue
}
-
- if query.Timestamps {
- frame.WriteString(line.Time.Format(time.RFC3339))
- frame.WriteString(" ")
+ header[0] = 1
+ case "stderr":
+ if !query.Stderr {
+ continue
}
- frame.WriteString(line.Msg)
+ header[0] = 2
+ default:
+ // Logging and moving on is the best we can do here. We may have already sent
+ // a Status and Content-Type to client therefore we can no longer report an error.
+ log.Infof("unknown Device type '%s' in log file from Container %s", line.Device, ctnr.ID())
+ continue
+ }
- binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
- if _, err := w.Write(header[0:8]); err != nil {
- log.Errorf("unable to write log output header: %q", err)
- }
- if _, err := io.WriteString(w, frame.String()); err != nil {
- log.Errorf("unable to write frame string: %q", err)
- }
- if flusher, ok := w.(http.Flusher); ok {
- flusher.Flush()
- }
+ if query.Timestamps {
+ frame.WriteString(line.Time.Format(time.RFC3339))
+ frame.WriteString(" ")
+ }
+ frame.WriteString(line.Msg)
+
+ binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
+ if _, err := w.Write(header[0:8]); err != nil {
+ log.Errorf("unable to write log output header: %q", err)
+ }
+ if _, err := io.WriteString(w, frame.String()); err != nil {
+ log.Errorf("unable to write frame string: %q", err)
+ }
+ if flusher, ok := w.(http.Flusher); ok {
+ flusher.Flush()
}
}
}
diff --git a/pkg/api/handlers/compat/exec.go b/pkg/api/handlers/compat/exec.go
index aee4196dd..a3b8cb573 100644
--- a/pkg/api/handlers/compat/exec.go
+++ b/pkg/api/handlers/compat/exec.go
@@ -173,21 +173,11 @@ func ExecStartHandler(w http.ResponseWriter, r *http.Request) {
return
}
- // Hijack the connection
- hijacker, ok := w.(http.Hijacker)
- if !ok {
- utils.InternalServerError(w, errors.Errorf("unable to hijack connection"))
- return
- }
-
- connection, buffer, err := hijacker.Hijack()
+ connection, buffer, err := AttachConnection(w, r)
if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "error hijacking connection"))
+ utils.InternalServerError(w, err)
return
}
-
- fmt.Fprintf(connection, AttachHeader)
-
logrus.Debugf("Hijack for attach of container %s exec session %s successful", sessionCtr.ID(), sessionID)
if err := sessionCtr.ExecHTTPStartAndAttach(sessionID, connection, buffer, nil, nil, nil); err != nil {
diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go
index b0fd932ba..53fe8952b 100644
--- a/pkg/api/server/handler_api.go
+++ b/pkg/api/server/handler_api.go
@@ -37,6 +37,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc {
c := context.WithValue(r.Context(), "decoder", s.Decoder) //nolint
c = context.WithValue(c, "runtime", s.Runtime) //nolint
c = context.WithValue(c, "shutdownFunc", s.Shutdown) //nolint
+ c = context.WithValue(c, "idletracker", s.idleTracker) //nolint
r = r.WithContext(c)
h(w, r)
diff --git a/pkg/api/server/idletracker/idletracker.go b/pkg/api/server/idletracker/idletracker.go
new file mode 100644
index 000000000..1ee905a99
--- /dev/null
+++ b/pkg/api/server/idletracker/idletracker.go
@@ -0,0 +1,74 @@
+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_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/api/server/server.go b/pkg/api/server/server.go
index 8af6d3186..1c6007745 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -10,12 +10,12 @@ import (
"runtime"
goRuntime "runtime"
"strings"
- "sync"
"syscall"
"time"
"github.com/containers/libpod/v2/libpod"
"github.com/containers/libpod/v2/pkg/api/handlers"
+ "github.com/containers/libpod/v2/pkg/api/server/idletracker"
"github.com/coreos/go-systemd/v22/activation"
"github.com/gorilla/mux"
"github.com/gorilla/schema"
@@ -24,14 +24,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 // 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 *idletracker.IdleTracker // 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
@@ -68,7 +68,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
}
router := mux.NewRouter().UseEncodedPath()
- idle := NewIdleTracker(duration)
+ idle := idletracker.NewIdleTracker(duration)
server := APIServer{
Server: http.Server{
@@ -231,55 +231,3 @@ func (s *APIServer) Shutdown() error {
func (s *APIServer) Close() error {
return s.Server.Close()
}
-
-type IdleTracker struct {
- active map[net.Conn]struct{}
- total int
- mux sync.Mutex
- timer *time.Timer
- Duration time.Duration
-}
-
-func NewIdleTracker(idle time.Duration) *IdleTracker {
- return &IdleTracker{
- active: 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 := len(t.active)
- logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, t.ActiveConnections(), t.TotalConnections())
- switch state {
- case http.StateNew, http.StateActive, http.StateHijacked:
- t.active[conn] = struct{}{}
- // stop the timer if we transitioned from idle
- if oldActive == 0 {
- t.timer.Stop()
- }
- t.total++
- case http.StateIdle, http.StateClosed:
- delete(t.active, conn)
- // Restart the timer if we've become idle
- if oldActive > 0 && len(t.active) == 0 {
- t.timer.Stop()
- t.timer.Reset(t.Duration)
- }
- }
-}
-
-func (t *IdleTracker) ActiveConnections() int {
- return len(t.active)
-}
-
-func (t *IdleTracker) TotalConnections() int {
- return t.total
-}
-
-func (t *IdleTracker) Done() <-chan time.Time {
- return t.timer.C
-}
diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index 077bb244f..297563688 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -457,15 +457,15 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A
switch {
case fd == 0:
- if streams.AttachOutput {
+ if streams.AttachInput {
+ // Write STDIN to STDOUT (echoing characters
+ // typed by another attach session)
if _, err := streams.OutputStream.Write(frame[0:l]); err != nil {
return err
}
}
case fd == 1:
- if streams.AttachInput {
- // Write STDIN to STDOUT (echoing characters
- // typed by another attach session)
+ if streams.AttachOutput {
if _, err := streams.OutputStream.Write(frame[0:l]); err != nil {
return err
}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 8023034ef..9e9b834ef 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -104,6 +104,7 @@ type PodRmReport struct {
type PodCreateOptions struct {
CGroupParent string
+ CreateCommand []string
Hostname string
Infra bool
InfraImage string
@@ -133,6 +134,7 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
}
s.InfraImage = p.InfraImage
s.SharedNamespaces = p.Share
+ s.PodCreateCommand = p.CreateCommand
// Networking config
s.NetNS = p.Net.Network
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 596fc2cc1..8909f831d 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -924,7 +924,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin
}
logChannel := make(chan *logs.LogLine, chSize)
- if err := ic.Libpod.Log(ctrs, logOpts, logChannel); err != nil {
+ if err := ic.Libpod.Log(ctx, ctrs, logOpts, logChannel); err != nil {
return err
}
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/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 855ca7ff9..435902ded 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -8,7 +8,6 @@ import (
"os/exec"
"path/filepath"
"strconv"
- "syscall"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/v2/libpod/define"
@@ -146,27 +145,6 @@ func movePauseProcessToScope() error {
return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope")
}
-func setRLimits() error { // nolint:deadcode,unused
- rlimits := new(syscall.Rlimit)
- rlimits.Cur = 1048576
- rlimits.Max = 1048576
- if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
- if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
- return errors.Wrapf(err, "error getting rlimits")
- }
- rlimits.Cur = rlimits.Max
- if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
- return errors.Wrapf(err, "error setting new rlimits")
- }
- }
- return nil
-}
-
-func setUMask() { // nolint:deadcode,unused
- // Be sure we can create directories with 0755 mode.
- syscall.Umask(0022)
-}
-
// checkInput can be used to verify any of the globalopt values
func checkInput() error { // nolint:deadcode,unused
return nil
@@ -252,13 +230,18 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System
}
}
- named, err := reference.ParseNormalizedNamed(name)
- if err != nil {
- return nil, err
- }
- repository = named.Name()
- if tagged, isTagged := named.(reference.NamedTagged); isTagged {
- tag = tagged.Tag()
+ if len(name) > 0 {
+ named, err := reference.ParseNormalizedNamed(name)
+ if err != nil {
+ return nil, err
+ }
+ repository = named.Name()
+ if tagged, isTagged := named.(reference.NamedTagged); isTagged {
+ tag = tagged.Tag()
+ }
+ } else {
+ repository = "<none>"
+ tag = "<none>"
}
report := entities.SystemDfImageReport{
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 879c66895..55c3238d2 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -406,10 +406,6 @@ func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, cre
return nil, err
}
- // Set the CreateCommand explicitly. Some (future) consumers of libpod
- // might not want to set it.
- options = append(options, libpod.WithCreateCommand())
-
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return nil, err
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 6f52b88b1..b974772d5 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -505,10 +505,9 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.
func addRlimits(config *CreateConfig, g *generate.Generator) error {
var (
- kernelMax uint64 = 1048576
- isRootless = rootless.IsRootless()
- nofileSet = false
- nprocSet = false
+ isRootless = rootless.IsRootless()
+ nofileSet = false
+ nprocSet = false
)
for _, u := range config.Resources.Ulimit {
@@ -538,8 +537,8 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
// files and number of processes to the maximum they can be set to
// (without overriding a sysctl)
if !nofileSet {
- max := kernelMax
- current := kernelMax
+ max := define.RLimitDefaultValue
+ current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil {
@@ -555,8 +554,8 @@ func addRlimits(config *CreateConfig, g *generate.Generator) error {
g.AddProcessRlimits("RLIMIT_NOFILE", max, current)
}
if !nprocSet {
- max := kernelMax
- current := kernelMax
+ max := define.RLimitDefaultValue
+ current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil {
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go
index 8063bee38..57dd2aba7 100644
--- a/pkg/specgen/container_validate.go
+++ b/pkg/specgen/container_validate.go
@@ -25,6 +25,15 @@ func exclusiveOptions(opt1, opt2 string) error {
// input for creating a container.
func (s *SpecGenerator) Validate() error {
+ if rootless.IsRootless() {
+ if s.StaticIP != nil || s.StaticIPv6 != nil {
+ return ErrNoStaticIPRootless
+ }
+ if s.StaticMAC != nil {
+ return ErrNoStaticMACRootless
+ }
+ }
+
//
// ContainerBasicConfig
//
@@ -65,10 +74,6 @@ func (s *SpecGenerator) Validate() error {
if len(s.CapAdd) > 0 && s.Privileged {
return exclusiveOptions("CapAdd", "privileged")
}
- // apparmor and privileged are exclusive
- if len(s.ApparmorProfile) > 0 && s.Privileged {
- return exclusiveOptions("AppArmorProfile", "privileged")
- }
// userns and idmappings conflict
if s.UserNS.IsPrivate() && s.IDMappings == nil {
return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace")
diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go
index 5d928cc5d..e445e6f0c 100644
--- a/pkg/specgen/generate/config_linux.go
+++ b/pkg/specgen/generate/config_linux.go
@@ -161,6 +161,7 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.
"/proc/scsi",
"/sys/firmware",
"/sys/fs/selinux",
+ "/sys/dev",
} {
g.AddLinuxMaskedPaths(mp)
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 57eaff355..be1e3b48e 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -78,7 +78,9 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
}
options := []libpod.CtrCreateOption{}
- options = append(options, libpod.WithCreateCommand())
+ if s.ContainerCreateCommand != nil {
+ options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand))
+ }
var newImage *image.Image
if s.Rootfs != "" {
@@ -104,7 +106,12 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
return nil, err
}
- opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, newImage)
+ command, err := makeCommand(ctx, s, newImage, rtc)
+ if err != nil {
+ return nil, err
+ }
+
+ opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, newImage, command)
if err != nil {
return nil, err
}
@@ -116,14 +123,14 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
}
options = append(options, libpod.WithExitCommand(exitCommandArgs))
- runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod)
+ runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command)
if err != nil {
return nil, err
}
return rt.NewContainer(ctx, runtimeSpec, options...)
}
-func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, img *image.Image) ([]libpod.CtrCreateOption, error) {
+func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, img *image.Image, command []string) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
@@ -138,7 +145,6 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
case "false":
break
case "", "true":
- command := s.Command
if len(command) == 0 {
command, err = img.Cmd(ctx)
if err != nil {
diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go
index 0a485e7cd..f279aac1c 100644
--- a/pkg/specgen/generate/oci.go
+++ b/pkg/specgen/generate/oci.go
@@ -20,10 +20,9 @@ import (
func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error {
var (
- kernelMax uint64 = 1048576
- isRootless = rootless.IsRootless()
- nofileSet = false
- nprocSet = false
+ isRootless = rootless.IsRootless()
+ nofileSet = false
+ nprocSet = false
)
if s.Rlimits == nil {
@@ -45,8 +44,8 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error {
// files and number of processes to the maximum they can be set to
// (without overriding a sysctl)
if !nofileSet {
- max := kernelMax
- current := kernelMax
+ max := define.RLimitDefaultValue
+ current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil {
@@ -62,8 +61,8 @@ func addRlimits(s *specgen.SpecGenerator, g *generate.Generator) error {
g.AddProcessRlimits("RLIMIT_NOFILE", max, current)
}
if !nprocSet {
- max := kernelMax
- current := kernelMax
+ max := define.RLimitDefaultValue
+ current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil {
@@ -87,7 +86,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image
finalCommand := []string{}
entrypoint := s.Entrypoint
- if len(entrypoint) == 0 && img != nil {
+ if entrypoint == nil && img != nil {
newEntry, err := img.Entrypoint(ctx)
if err != nil {
return nil, err
@@ -126,7 +125,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image
return finalCommand, nil
}
-func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod) (*spec.Spec, error) {
+func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) {
var (
inUserNS bool
)
@@ -252,10 +251,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
g.SetProcessCwd(s.WorkDir)
- finalCmd, err := makeCommand(ctx, s, newImage, rtc)
- if err != nil {
- return nil, err
- }
g.SetProcessArgs(finalCmd)
g.SetProcessTerminal(s.Terminal)
@@ -290,13 +285,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
}
- // SECURITY OPTS
- g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
-
- if !s.Privileged {
- g.SetProcessApparmorProfile(s.ApparmorProfile)
- }
-
BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g)
for name, val := range s.Env {
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index 690651a23..4fe1b6435 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -93,7 +93,9 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er
options = append(options, libpod.WithInfraContainerPorts(ports))
}
options = append(options, libpod.WithPodCgroups())
- options = append(options, libpod.WithPodCreateCommand())
+ if p.PodCreateCommand != nil {
+ options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand))
+ }
if len(p.InfraConmonPidFile) > 0 {
options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile))
}
diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go
index 9412ecfbf..c8d1c27c5 100644
--- a/pkg/specgen/generate/ports.go
+++ b/pkg/specgen/generate/ports.go
@@ -356,6 +356,7 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) {
splitProto := strings.Split(protocol, ",")
// Don't error on duplicates - just deduplicate
for _, p := range splitProto {
+ p = strings.ToLower(p)
switch p {
case protoTCP, "":
protocols[protoTCP] = struct{}{}
diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go
index 70493cd5f..fcd1622f9 100644
--- a/pkg/specgen/generate/security.go
+++ b/pkg/specgen/generate/security.go
@@ -3,6 +3,7 @@ package generate
import (
"strings"
+ "github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/v2/libpod"
@@ -56,6 +57,28 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s
return nil
}
+func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error {
+ hasProfile := len(s.ApparmorProfile) > 0
+ if !apparmor.IsEnabled() {
+ if hasProfile {
+ return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile)
+ }
+ return nil
+ }
+ // If privileged and caller did not specify apparmor profiles return
+ if s.Privileged && !hasProfile {
+ return nil
+ }
+ if !hasProfile {
+ s.ApparmorProfile = rtc.Containers.ApparmorProfile
+ }
+ if len(s.ApparmorProfile) > 0 {
+ g.SetProcessApparmorProfile(s.ApparmorProfile)
+ }
+
+ return nil
+}
+
func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image, rtc *config.Config) error {
var (
caplist []string
@@ -105,6 +128,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
}
}
+
+ g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
+
+ if err := setupApparmor(s, rtc, g); err != nil {
+ return err
+ }
+
configSpec := g.Config
configSpec.Process.Capabilities.Bounding = caplist
diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go
index 070bb1e41..69c3b58ed 100644
--- a/pkg/specgen/pod_validate.go
+++ b/pkg/specgen/pod_validate.go
@@ -1,6 +1,7 @@
package specgen
import (
+ "github.com/containers/libpod/v2/pkg/rootless"
"github.com/containers/libpod/v2/pkg/util"
"github.com/pkg/errors"
)
@@ -18,6 +19,16 @@ func exclusivePodOptions(opt1, opt2 string) error {
// Validate verifies the input is valid
func (p *PodSpecGenerator) Validate() error {
+
+ if rootless.IsRootless() {
+ if p.StaticIP != nil {
+ return ErrNoStaticIPRootless
+ }
+ if p.StaticMAC != nil {
+ return ErrNoStaticMACRootless
+ }
+ }
+
// PodBasicConfig
if p.NoInfra {
if len(p.InfraCommand) > 0 {
diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
index 600d27004..3c32ec365 100644
--- a/pkg/specgen/podspecgen.go
+++ b/pkg/specgen/podspecgen.go
@@ -49,6 +49,12 @@ type PodBasicConfig struct {
// Conflicts with NoInfra=true.
// Optional.
SharedNamespaces []string `json:"shared_namespaces,omitempty"`
+ // PodCreateCommand is the command used to create this pod.
+ // This will be shown in the output of Inspect() on the pod, and may
+ // also be used by some tools that wish to recreate the pod
+ // (e.g. `podman generate systemd --new`).
+ // Optional.
+ PodCreateCommand []string `json:"pod_create_command,omitempty"`
}
// PodNetworkConfig contains networking configuration for a pod.
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 327c15c5a..bd738f5a7 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -1,6 +1,7 @@
package specgen
import (
+ "errors"
"net"
"syscall"
@@ -130,6 +131,13 @@ type ContainerBasicConfig struct {
// Remove indicates if the container should be removed once it has been started
// and exits
Remove bool `json:"remove,omitempty"`
+ // ContainerCreateCommand is the command that was used to create this
+ // container.
+ // This will be shown in the output of Inspect() on the container, and
+ // may also be used by some tools that wish to recreate the container
+ // (e.g. `podman generate systemd --new`).
+ // Optional.
+ ContainerCreateCommand []string `json:"containerCreateCommand,omitempty"`
}
// ContainerStorageConfig contains information on the storage configuration of a
@@ -449,6 +457,15 @@ type PortMapping struct {
Protocol string `json:"protocol,omitempty"`
}
+var (
+ // ErrNoStaticIPRootless is used when a rootless user requests to assign a static IP address
+ // to a pod or container
+ ErrNoStaticIPRootless error = errors.New("rootless containers and pods cannot be assigned static IP addresses")
+ // ErrNoStaticMACRootless is used when a rootless user requests to assign a static MAC address
+ // to a pod or container
+ ErrNoStaticMACRootless error = errors.New("rootless containers and pods cannot be assigned static MAC addresses")
+)
+
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
func NewSpecGenerator(arg string, rootfs bool) *SpecGenerator {
csc := ContainerStorageConfig{}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 47d3e231d..9eeb116c0 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -555,7 +555,7 @@ func ValidatePullType(pullType string) (PullType, error) {
switch pullType {
case "always":
return PullImageAlways, nil
- case "missing":
+ case "missing", "IfNotPresent":
return PullImageMissing, nil
case "never":
return PullImageNever, nil
@@ -641,7 +641,7 @@ func ValidateSysctls(strSlice []string) (map[string]string, error) {
}
}
if !foundMatch {
- return nil, errors.Errorf("sysctl '%s' is not whitelisted", arr[0])
+ return nil, errors.Errorf("sysctl '%s' is not allowed", arr[0])
}
}
return sysctl, nil
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 8650ba000..07b492331 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -754,7 +754,7 @@ func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- if err := i.Runtime.Log(containers, &options, logChannel); err != nil {
+ if err := i.Runtime.Log(getContext(), containers, &options, logChannel); err != nil {
return err
}
go func() {
diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go
index 5c5f075f7..ac93939d9 100644
--- a/pkg/varlinkapi/create.go
+++ b/pkg/varlinkapi/create.go
@@ -915,10 +915,6 @@ func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, cre
return nil, err
}
- // Set the CreateCommand explicitly. Some (future) consumers of libpod
- // might not want to set it.
- options = append(options, libpod.WithCreateCommand())
-
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return nil, err
diff --git a/rootless.md b/rootless.md
index e5b71f0b9..e6f17a370 100644
--- a/rootless.md
+++ b/rootless.md
@@ -30,7 +30,7 @@ can easily fail
* Only other supported driver is VFS.
* No CNI Support
* CNI wants to modify IPTables, plus other network manipulation that requires CAP_SYS_ADMIN.
- * There is potential we could probably do some sort of blacklisting of the relevant plugins, and add a new plugin for rootless networking - slirp4netns as one example and there may be others
+ * There is potential we could probably do some sort of denylisting of the relevant plugins, and add a new plugin for rootless networking - slirp4netns as one example and there may be others
* Cannot use ping out of the box.
* [(Can be fixed by setting sysctl on host)](https://github.com/containers/libpod/blob/master/troubleshooting.md#6-rootless-containers-cannot-ping-hosts)
* Requires new shadow-utils (not found in older (RHEL7/Centos7 distros) Should be fixed in RHEL7.7 release)
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 6633f3a53..acd28170b 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -591,3 +591,7 @@ func SkipIfNotFedora() {
ginkgo.Skip("Test can only run on Fedora")
}
}
+
+func isRootless() bool {
+ return os.Geteuid() != 0
+}
diff --git a/test/e2e/create_staticip_test.go b/test/e2e/create_staticip_test.go
index 995193a7d..261792a39 100644
--- a/test/e2e/create_staticip_test.go
+++ b/test/e2e/create_staticip_test.go
@@ -6,6 +6,7 @@ import (
"os"
"time"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -19,7 +20,6 @@ var _ = Describe("Podman create with --ip flag", func() {
)
BeforeEach(func() {
- SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -39,18 +39,21 @@ var _ = Describe("Podman create with --ip flag", func() {
})
It("Podman create --ip with garbage address", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "114232346", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result).To(ExitWithError())
})
It("Podman create --ip with v6 address", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "2001:db8:bad:beef::1", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result).To(ExitWithError())
})
It("Podman create --ip with non-allocatable IP", func() {
+ SkipIfRootless()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
@@ -64,19 +67,25 @@ var _ = Describe("Podman create with --ip flag", func() {
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", ip, ALPINE, "ip", "addr"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ // Rootless static ip assignment should error
+ if rootless.IsRootless() {
+ Expect(result.ExitCode()).To(Equal(125))
+ } else {
+ Expect(result.ExitCode()).To(Equal(0))
- result = podmanTest.Podman([]string{"start", "test"})
- result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
+ result = podmanTest.Podman([]string{"start", "test"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
- result = podmanTest.Podman([]string{"logs", "test"})
- result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
- Expect(result.OutputToString()).To(ContainSubstring(ip + "/16"))
+ result = podmanTest.Podman([]string{"logs", "test"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring(ip + "/16"))
+ }
})
It("Podman create two containers with the same IP", func() {
+ SkipIfRootless()
ip := GetRandomIPAddress()
result := podmanTest.Podman([]string{"create", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"})
result.WaitWithDefaultTimeout()
diff --git a/test/e2e/create_staticmac_test.go b/test/e2e/create_staticmac_test.go
index 93af5ab10..33675d607 100644
--- a/test/e2e/create_staticmac_test.go
+++ b/test/e2e/create_staticmac_test.go
@@ -1,10 +1,9 @@
-// +build !remoteclient
-
package integration
import (
"os"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -18,7 +17,6 @@ var _ = Describe("Podman run with --mac-address flag", func() {
)
BeforeEach(func() {
- SkipIfRootless()
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
@@ -40,7 +38,11 @@ var _ = Describe("Podman run with --mac-address flag", func() {
It("Podman run --mac-address", func() {
result := podmanTest.Podman([]string{"run", "--mac-address", "92:d0:c6:0a:29:34", ALPINE, "ip", "addr"})
result.WaitWithDefaultTimeout()
- Expect(result.ExitCode()).To(Equal(0))
- Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34"))
+ if rootless.IsRootless() {
+ Expect(result.ExitCode()).To(Equal(125))
+ } else {
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34"))
+ }
})
})
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index 5a519413e..736376207 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -98,7 +98,6 @@ var _ = Describe("Podman exec", func() {
It("podman exec os.Setenv env", func() {
// remote doesn't properly interpret os.Setenv
- SkipIfRemote()
setup := podmanTest.RunTopContainer("test1")
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index e38ace53f..8aa9712fd 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -11,6 +11,7 @@ import (
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
)
var _ = Describe("Podman Info", func() {
@@ -35,11 +36,30 @@ var _ = Describe("Podman Info", func() {
processTestResult(f)
})
- It("podman info json output", func() {
- session := podmanTest.Podman([]string{"info", "--format=json"})
- session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(0))
-
+ It("podman info --format json", func() {
+ tests := []struct {
+ input string
+ success bool
+ exitCode int
+ }{
+ {"json", true, 0},
+ {" json", true, 0},
+ {"json ", true, 0},
+ {" json ", true, 0},
+ {"{{json .}}", true, 0},
+ {"{{ json .}}", true, 0},
+ {"{{json . }}", true, 0},
+ {" {{ json . }} ", true, 0},
+ {"{{json }}", false, 125},
+ {"{{json .", false, 125},
+ {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ }
+ for _, tt := range tests {
+ session := podmanTest.Podman([]string{"info", "--format", tt.input})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(tt.exitCode))
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+ }
})
It("podman info --format GO template", func() {
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index cb1bae16d..ad0f8023b 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -28,11 +28,6 @@ func SkipIfRootless() {
ginkgo.Skip("This function is not enabled for rootless podman")
}
}
-func SkipIfRootlessV2() {
- if os.Geteuid() != 0 {
- ginkgo.Skip("This function is not enabled for v2 rootless podman")
- }
-}
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 63d5eff21..b1844b917 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/pod_create_test.go b/test/e2e/pod_create_test.go
index 57737ad59..016eaaa99 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/v2/pkg/rootless"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -238,17 +239,20 @@ var _ = Describe("Podman pod create", func() {
})
It("podman create pod with IP address", func() {
- SkipIfRootless()
name := "test"
ip := GetRandomIPAddress()
podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name})
podCreate.WaitWithDefaultTimeout()
- Expect(podCreate.ExitCode()).To(Equal(0))
-
- podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
- podResolvConf.WaitWithDefaultTimeout()
- Expect(podResolvConf.ExitCode()).To(Equal(0))
- Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue())
+ // Rootless should error
+ if rootless.IsRootless() {
+ Expect(podCreate.ExitCode()).To(Equal(125))
+ } else {
+ Expect(podCreate.ExitCode()).To(Equal(0))
+ podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
+ podResolvConf.WaitWithDefaultTimeout()
+ Expect(podResolvConf.ExitCode()).To(Equal(0))
+ Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue())
+ }
})
It("podman create pod with IP address and no infra should fail", func() {
@@ -262,17 +266,20 @@ var _ = Describe("Podman pod create", func() {
It("podman create pod with MAC address", func() {
SkipIfRemote()
- SkipIfRootless()
name := "test"
mac := "92:d0:c6:0a:29:35"
podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name})
podCreate.WaitWithDefaultTimeout()
- Expect(podCreate.ExitCode()).To(Equal(0))
-
- podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
- podResolvConf.WaitWithDefaultTimeout()
- Expect(podResolvConf.ExitCode()).To(Equal(0))
- Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue())
+ // Rootless should error
+ if rootless.IsRootless() {
+ Expect(podCreate.ExitCode()).To(Equal(125))
+ } else {
+ Expect(podCreate.ExitCode()).To(Equal(0))
+ podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"})
+ podResolvConf.WaitWithDefaultTimeout()
+ Expect(podResolvConf.ExitCode()).To(Equal(0))
+ Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue())
+ }
})
It("podman create pod with MAC address and no infra should fail", func() {
diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go
index 5e3634435..16bf1c4c9 100644
--- a/test/e2e/pod_inspect_test.go
+++ b/test/e2e/pod_inspect_test.go
@@ -1,8 +1,11 @@
package integration
import (
+ "encoding/json"
"os"
+ "github.com/containers/libpod/v2/libpod/define"
+
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -79,4 +82,22 @@ var _ = Describe("Podman pod inspect", func() {
index := len(inspectCreateCommand) - len(createCommand)
Expect(inspectCreateCommand[index:]).To(Equal(createCommand))
})
+
+ It("podman pod inspect outputs port bindings", func() {
+ podName := "testPod"
+ create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8080:80"})
+ create.WaitWithDefaultTimeout()
+ Expect(create.ExitCode()).To(Equal(0))
+
+ inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName})
+ inspectOut.WaitWithDefaultTimeout()
+ Expect(inspectOut.ExitCode()).To(Equal(0))
+
+ inspectJSON := new(define.InspectPodData)
+ err := json.Unmarshal(inspectOut.Out.Contents(), inspectJSON)
+ Expect(err).To(BeNil())
+ Expect(inspectJSON.InfraConfig).To(Not(BeNil()))
+ Expect(len(inspectJSON.InfraConfig.PortBindings["80/tcp"])).To(Equal(1))
+ Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8080"))
+ })
})
diff --git a/test/e2e/run_apparmor_test.go b/test/e2e/run_apparmor_test.go
new file mode 100644
index 000000000..344309b93
--- /dev/null
+++ b/test/e2e/run_apparmor_test.go
@@ -0,0 +1,158 @@
+// +build !remote
+
+package integration
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/containers/common/pkg/apparmor"
+ . "github.com/containers/libpod/v2/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+func skipIfAppArmorEnabled() {
+ if apparmor.IsEnabled() {
+ Skip("Apparmor is enabled")
+ }
+}
+func skipIfAppArmorDisabled() {
+ if !apparmor.IsEnabled() {
+ Skip("Apparmor is not enabled")
+ }
+}
+
+var _ = Describe("Podman run", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.SeedImages()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman run apparmor default", func() {
+ skipIfAppArmorDisabled()
+ session := podmanTest.Podman([]string{"create", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal(apparmor.Profile))
+ })
+
+ It("podman run no apparmor --privileged", func() {
+ skipIfAppArmorDisabled()
+ session := podmanTest.Podman([]string{"create", "--privileged", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal(""))
+ })
+
+ It("podman run no apparmor --security-opt=apparmor.Profile --privileged", func() {
+ skipIfAppArmorDisabled()
+ session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), "--privileged", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal(apparmor.Profile))
+ })
+
+ It("podman run apparmor aa-test-profile", func() {
+ skipIfAppArmorDisabled()
+ aaProfile := `
+#include <tunables/global>
+profile aa-test-profile flags=(attach_disconnected,mediate_deleted) {
+ #include <abstractions/base>
+ deny mount,
+ deny /sys/[^f]*/** wklx,
+ deny /sys/f[^s]*/** wklx,
+ deny /sys/fs/[^c]*/** wklx,
+ deny /sys/fs/c[^g]*/** wklx,
+ deny /sys/fs/cg[^r]*/** wklx,
+ deny /sys/firmware/efi/efivars/** rwklx,
+ deny /sys/kernel/security/** rwklx,
+}
+`
+ aaFile := filepath.Join(os.TempDir(), "aaFile")
+ Expect(ioutil.WriteFile(aaFile, []byte(aaProfile), 0755)).To(BeNil())
+ parse := SystemExec("apparmor_parser", []string{"-Kr", aaFile})
+ Expect(parse.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=aa-test-profile", "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal("aa-test-profile"))
+ })
+
+ It("podman run apparmor invalid", func() {
+ skipIfAppArmorDisabled()
+ session := podmanTest.Podman([]string{"run", "--security-opt", "apparmor=invalid", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman run apparmor unconfined", func() {
+ skipIfAppArmorDisabled()
+ session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal("unconfined"))
+ })
+
+ It("podman run apparmor disabled --security-opt apparmor fails", func() {
+ skipIfAppArmorEnabled()
+ // Should fail if user specifies apparmor on disabled system
+ session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman run apparmor disabled no default", func() {
+ skipIfAppArmorEnabled()
+ // Should succeed if user specifies apparmor on disabled system
+ session := podmanTest.Podman([]string{"create", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ cid := session.OutputToString()
+ // Verify that apparmor.Profile is being set
+ inspect := podmanTest.InspectContainer(cid)
+ Expect(inspect[0].AppArmorProfile).To(Equal(""))
+ })
+})
diff --git a/test/e2e/run_entrypoint_test.go b/test/e2e/run_entrypoint_test.go
index c947fa863..e6604a21e 100644
--- a/test/e2e/run_entrypoint_test.go
+++ b/test/e2e/run_entrypoint_test.go
@@ -101,6 +101,11 @@ ENTRYPOINT ["grep", "Alpine", "/etc/os-release"]
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue())
+
+ session = podmanTest.Podman([]string{"run", "--entrypoint", "", "foobar.com/entrypoint:latest", "uname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.LineInOuputStartsWith("Linux")).To(BeTrue())
})
It("podman run user entrypoint with command overrides image entrypoint and image cmd", func() {
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 6c049c5c1..9357145ab 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -88,6 +88,20 @@ var _ = Describe("Podman run networking", func() {
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
+ It("podman run -p 8080:80/TCP", func() {
+ name := "testctr"
+ // "TCP" in upper characters
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80/TCP", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
+ // "tcp" in lower characters
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080"))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ })
+
It("podman run -p 80/udp", func() {
name := "testctr"
session := podmanTest.Podman([]string{"create", "-t", "-p", "80/udp", "--name", name, ALPINE, "/bin/sh"})
diff --git a/test/e2e/system_df_test.go b/test/e2e/system_df_test.go
index e882756f9..3d7aaf659 100644
--- a/test/e2e/system_df_test.go
+++ b/test/e2e/system_df_test.go
@@ -60,4 +60,18 @@ var _ = Describe("podman system df", func() {
Expect(containers[1]).To(Equal("2"))
Expect(volumes[2]).To(Equal("1"))
})
+
+ It("podman system df image with no tag", func() {
+ session := podmanTest.PodmanNoCache([]string{"create", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.PodmanNoCache([]string{"image", "untag", ALPINE})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.PodmanNoCache([]string{"system", "df"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go
index 143b8f59f..05c0d3fd0 100644
--- a/test/e2e/systemd_test.go
+++ b/test/e2e/systemd_test.go
@@ -112,5 +112,40 @@ WantedBy=multi-user.target
systemctl.WaitWithDefaultTimeout()
Expect(systemctl.ExitCode()).To(Equal(0))
Expect(strings.Contains(systemctl.OutputToString(), "State:")).To(BeTrue())
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
+ })
+
+ It("podman create container with systemd entrypoint triggers systemd mode", func() {
+ ctrName := "testCtr"
+ run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", ubi_init})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
+ })
+
+ It("podman create container with systemd=always triggers systemd mode", func() {
+ ctrName := "testCtr"
+ run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE})
+ run.WaitWithDefaultTimeout()
+ Expect(run.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"inspect", ctrName})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(len(conData)).To(Equal(1))
+ Expect(conData[0].Config.SystemdMode).To(BeTrue())
})
})
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 775be6511..eb1d1b733 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -55,17 +55,29 @@ var _ = Describe("Podman version", func() {
})
It("podman version --format json", func() {
- session := podmanTest.Podman([]string{"version", "--format", "json"})
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- Expect(session.IsJSONOutputValid()).To(BeTrue())
- })
-
- It("podman version --format json", func() {
- session := podmanTest.Podman([]string{"version", "--format", "{{ json .}}"})
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- Expect(session.IsJSONOutputValid()).To(BeTrue())
+ tests := []struct {
+ input string
+ success bool
+ exitCode int
+ }{
+ {"json", true, 0},
+ {" json", true, 0},
+ {"json ", true, 0},
+ {" json ", true, 0},
+ {"{{json .}}", true, 0},
+ {"{{ json .}}", true, 0},
+ {"{{json . }}", true, 0},
+ {" {{ json . }} ", true, 0},
+ {"{{json }}", false, 125},
+ {"{{json .", false, 125},
+ {"json . }}", false, 0}, // Note: this does NOT fail but produces garbage
+ }
+ for _, tt := range tests {
+ session := podmanTest.Podman([]string{"version", "--format", tt.input})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(tt.exitCode))
+ Expect(session.IsJSONOutputValid()).To(Equal(tt.success))
+ }
})
It("podman version --format GO template", func() {
diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats
index 9a6b39057..478ff06bb 100644
--- a/test/system/200-pod.bats
+++ b/test/system/200-pod.bats
@@ -153,9 +153,13 @@ function random_ip() {
# Create a pod with all the desired options
# FIXME: --ip=$ip fails:
# Error adding network: failed to allocate all requested IPs
+ local mac_option="--mac-address=$mac"
+ if is_rootless; then
+ mac_option=
+ fi
run_podman pod create --name=mypod \
--pod-id-file=$pod_id_file \
- --mac-address=$mac \
+ $mac_option \
--hostname=$hostname \
--add-host "$add_host_n:$add_host_ip" \
--dns "$dns_server" \
@@ -168,7 +172,7 @@ function random_ip() {
is "$(<$pod_id_file)" "$pod_id" "contents of pod-id-file"
# Check each of the options
- if ! is_rootless; then
+ if [ -n "$mac_option" ]; then
run_podman run --rm --pod mypod $IMAGE ip link show
# 'ip' outputs hex in lower-case, ${expr,,} converts UC to lc
is "$output" ".* link/ether ${mac,,} " "requested MAC address was set"