summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/completion.go51
-rw-r--r--cmd/podman/common/specgen.go53
-rw-r--r--cmd/podman/containers/prune.go2
-rw-r--r--cmd/podman/images/prune.go4
-rw-r--r--cmd/podman/networks/prune.go3
-rw-r--r--cmd/podman/system/prune.go3
-rw-r--r--docs/source/markdown/podman-create.1.md3
-rw-r--r--docs/source/markdown/podman-run.1.md3
-rw-r--r--docs/source/markdown/podman-volume-ls.1.md9
-rwxr-xr-xhack/branch_commits.rb98
-rw-r--r--libpod/container.go14
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go4
-rw-r--r--libpod/container_internal.go29
-rw-r--r--libpod/container_internal_linux.go46
-rw-r--r--libpod/define/container_inspect.go15
-rw-r--r--libpod/diff.go5
-rw-r--r--libpod/options.go17
-rw-r--r--libpod/runtime.go15
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--pkg/domain/infra/abi/play.go3
-rw-r--r--pkg/domain/infra/abi/secrets.go12
-rw-r--r--pkg/specgen/generate/container_create.go19
-rw-r--r--pkg/specgen/generate/kube/kube.go42
-rw-r--r--pkg/specgen/specgen.go9
-rw-r--r--test/e2e/run_selinux_test.go8
-rw-r--r--test/e2e/run_test.go48
-rw-r--r--test/system/030-run.bats14
-rw-r--r--test/system/700-play.bats26
29 files changed, 457 insertions, 102 deletions
diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index 4aca79770..de5b2995a 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/network"
"github.com/containers/podman/v3/pkg/registries"
"github.com/containers/podman/v3/pkg/rootless"
systemdDefine "github.com/containers/podman/v3/pkg/systemd/define"
@@ -243,7 +244,7 @@ func getRegistries() ([]string, cobra.ShellCompDirective) {
return regs, cobra.ShellCompDirectiveNoFileComp
}
-func getNetworks(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) {
+func getNetworks(cmd *cobra.Command, toComplete string, cType completeType) ([]string, cobra.ShellCompDirective) {
suggestions := []string{}
networkListOptions := entities.NetworkListOptions{}
@@ -259,7 +260,15 @@ func getNetworks(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCo
}
for _, n := range networks {
- if strings.HasPrefix(n.Name, toComplete) {
+ id := network.GetNetworkID(n.Name)
+ // include ids in suggestions if cType == completeIDs or
+ // more then 2 chars are typed and cType == completeDefault
+ if ((len(toComplete) > 1 && cType == completeDefault) ||
+ cType == completeIDs) && strings.HasPrefix(id, toComplete) {
+ suggestions = append(suggestions, id[0:12])
+ }
+ // include name in suggestions
+ if cType != completeIDs && strings.HasPrefix(n.Name, toComplete) {
suggestions = append(suggestions, n.Name)
}
}
@@ -502,7 +511,7 @@ func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string)
if !validCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
- return getNetworks(cmd, toComplete)
+ return getNetworks(cmd, toComplete, completeDefault)
}
// AutocompleteDefaultOneArg - Autocomplete path only for the first argument.
@@ -588,7 +597,7 @@ func AutocompleteContainerOneArg(cmd *cobra.Command, args []string, toComplete s
// AutocompleteNetworkConnectCmd - Autocomplete podman network connect/disconnect command args.
func AutocompleteNetworkConnectCmd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
- return getNetworks(cmd, toComplete)
+ return getNetworks(cmd, toComplete, completeDefault)
}
if len(args) == 1 {
return getContainers(cmd, toComplete, completeDefault)
@@ -624,7 +633,7 @@ func AutocompleteInspect(cmd *cobra.Command, args []string, toComplete string) (
containers, _ := getContainers(cmd, toComplete, completeDefault)
images, _ := getImages(cmd, toComplete)
pods, _ := getPods(cmd, toComplete, completeDefault)
- networks, _ := getNetworks(cmd, toComplete)
+ networks, _ := getNetworks(cmd, toComplete, completeDefault)
volumes, _ := getVolumes(cmd, toComplete)
suggestions := append(containers, images...)
suggestions = append(suggestions, pods...)
@@ -885,7 +894,7 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin
},
}
- networks, _ := getNetworks(cmd, toComplete)
+ networks, _ := getNetworks(cmd, toComplete, completeDefault)
suggestions, dir := completeKeyValues(toComplete, kv)
// add slirp4netns here it does not work correct if we add it to the kv map
suggestions = append(suggestions, "slirp4netns")
@@ -1039,7 +1048,10 @@ func AutocompleteNetworkDriver(cmd *cobra.Command, args []string, toComplete str
// -> "ipc", "net", "pid", "user", "uts", "cgroup", "none"
func AutocompletePodShareNamespace(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
namespaces := []string{"ipc", "net", "pid", "user", "uts", "cgroup", "none"}
- return namespaces, cobra.ShellCompDirectiveNoFileComp
+ split := strings.Split(toComplete, ",")
+ split[len(split)-1] = ""
+ toComplete = strings.Join(split, ",")
+ return prefixSlice(toComplete, namespaces), cobra.ShellCompDirectiveNoFileComp
}
// AutocompletePodPsSort - Autocomplete images sort options.
@@ -1115,7 +1127,7 @@ func AutocompletePsFilters(cmd *cobra.Command, args []string, toComplete string)
return []string{define.HealthCheckHealthy,
define.HealthCheckUnhealthy}, cobra.ShellCompDirectiveNoFileComp
},
- "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) },
+ "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
"label=": nil,
"exited=": nil,
"until=": nil,
@@ -1138,7 +1150,7 @@ func AutocompletePodPsFilters(cmd *cobra.Command, args []string, toComplete stri
"ctr-status=": func(_ string) ([]string, cobra.ShellCompDirective) {
return containerStatuses, cobra.ShellCompDirectiveNoFileComp
},
- "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) },
+ "network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
"label=": nil,
}
return completeKeyValues(toComplete, kv)
@@ -1158,11 +1170,28 @@ func AutocompleteImageFilters(cmd *cobra.Command, args []string, toComplete stri
return completeKeyValues(toComplete, kv)
}
+// AutocompletePruneFilters - Autocomplete container/image prune --filter options.
+func AutocompletePruneFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ kv := keyValueCompletion{
+ "until=": nil,
+ "label=": nil,
+ }
+ return completeKeyValues(toComplete, kv)
+}
+
// AutocompleteNetworkFilters - Autocomplete network ls --filter options.
func AutocompleteNetworkFilters(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
kv := keyValueCompletion{
- "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s) },
- "plugin=": nil,
+ "name=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeNames) },
+ "id=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeIDs) },
+ "plugin=": func(_ string) ([]string, cobra.ShellCompDirective) {
+ return []string{"bridge", "portmap",
+ "firewall", "tuning", "dnsname", "macvlan"}, cobra.ShellCompDirectiveNoFileComp
+ },
+ "label=": nil,
+ "driver=": func(_ string) ([]string, cobra.ShellCompDirective) {
+ return []string{"bridge"}, cobra.ShellCompDirectiveNoFileComp
+ },
}
return completeKeyValues(toComplete, kv)
}
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 5dc2ec864..2f45e559d 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -778,20 +778,30 @@ func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrot
return td, nil
}
-func parseSecrets(secrets []string) ([]string, map[string]string, error) {
+func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) {
secretParseError := errors.New("error parsing secret")
- var mount []string
+ var mount []specgen.Secret
envs := make(map[string]string)
for _, val := range secrets {
+ // mount only tells if user has set an option that can only be used with mount secret type
+ mountOnly := false
source := ""
secretType := ""
target := ""
+ var uid, gid uint32
+ // default mode 444 octal = 292 decimal
+ var mode uint32 = 292
split := strings.Split(val, ",")
// --secret mysecret
if len(split) == 1 {
- source = val
- mount = append(mount, source)
+ mountSecret := specgen.Secret{
+ Source: val,
+ UID: uid,
+ GID: gid,
+ Mode: mode,
+ }
+ mount = append(mount, mountSecret)
continue
}
// --secret mysecret,opt=opt
@@ -799,7 +809,7 @@ func parseSecrets(secrets []string) ([]string, map[string]string, error) {
source = split[0]
split = split[1:]
}
- // TODO: implement other secret options
+
for _, val := range split {
kv := strings.SplitN(val, "=", 2)
if len(kv) < 2 {
@@ -818,6 +828,28 @@ func parseSecrets(secrets []string) ([]string, map[string]string, error) {
secretType = kv[1]
case "target":
target = kv[1]
+ case "mode":
+ mountOnly = true
+ mode64, err := strconv.ParseUint(kv[1], 8, 32)
+ if err != nil {
+ return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1])
+ }
+ mode = uint32(mode64)
+ case "uid", "UID":
+ mountOnly = true
+ uid64, err := strconv.ParseUint(kv[1], 10, 32)
+ if err != nil {
+ return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1])
+ }
+ uid = uint32(uid64)
+ case "gid", "GID":
+ mountOnly = true
+ gid64, err := strconv.ParseUint(kv[1], 10, 32)
+ if err != nil {
+ return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1])
+ }
+ gid = uint32(gid64)
+
default:
return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val)
}
@@ -833,9 +865,18 @@ func parseSecrets(secrets []string) ([]string, map[string]string, error) {
if target != "" {
return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets")
}
- mount = append(mount, source)
+ mountSecret := specgen.Secret{
+ Source: source,
+ UID: uid,
+ GID: gid,
+ Mode: mode,
+ }
+ mount = append(mount, mountSecret)
}
if secretType == "env" {
+ if mountOnly {
+ return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env")
+ }
if target == "" {
target = source
}
diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go
index 837d90f70..94da029b9 100644
--- a/cmd/podman/containers/prune.go
+++ b/cmd/podman/containers/prune.go
@@ -43,7 +43,7 @@ func init() {
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation. The default is false")
filterFlagName := "filter"
flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
- _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePruneFilters)
}
func prune(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go
index 6849d5971..db645cc2e 100644
--- a/cmd/podman/images/prune.go
+++ b/cmd/podman/images/prune.go
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
"github.com/containers/podman/v3/cmd/podman/validate"
@@ -44,8 +45,7 @@ func init() {
filterFlagName := "filter"
flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
- //TODO: add completion for filters
- _ = pruneCmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = pruneCmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePruneFilters)
}
func prune(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go
index bcc55f0f4..5f1cbda5f 100644
--- a/cmd/podman/networks/prune.go
+++ b/cmd/podman/networks/prune.go
@@ -6,7 +6,6 @@ import (
"os"
"strings"
- "github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
@@ -39,7 +38,7 @@ func networkPruneFlags(cmd *cobra.Command, flags *pflag.FlagSet) {
flags.BoolVarP(&force, "force", "f", false, "do not prompt for confirmation")
filterFlagName := "filter"
flags.StringArrayVar(&filter, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
- _ = cmd.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePruneFilters)
}
func init() {
diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go
index 3020a541b..0f1285564 100644
--- a/cmd/podman/system/prune.go
+++ b/cmd/podman/system/prune.go
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/utils"
@@ -50,7 +51,7 @@ func init() {
flags.BoolVar(&pruneOptions.Volume, "volumes", false, "Prune volumes")
filterFlagName := "filter"
flags.StringArrayVar(&filters, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
- _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, completion.AutocompleteNone)
+ _ = pruneCommand.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePruneFilters)
}
func prune(cmd *cobra.Command, args []string) error {
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index d03d48506..2c51b312d 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -859,6 +859,9 @@ Secret Options
- `type=mount|env` : How the secret will be exposed to the container. Default mount.
- `target=target` : Target of secret. Defauts to secret name.
+- `uid=0` : UID of secret. Defaults to 0. Mount secret type only.
+- `gid=0` : GID of secret. Defaults to 0. Mount secret type only.
+- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only.
#### **--security-opt**=*option*
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index b9cfb01d1..46e15d62f 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -911,6 +911,9 @@ Secret Options
- `type=mount|env` : How the secret will be exposed to the container. Default mount.
- `target=target` : Target of secret. Defauts to secret name.
+- `uid=0` : UID of secret. Defaults to 0. Mount secret type only.
+- `gid=0` : GID of secret. Defaults to 0. Mount secret type only.
+- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only.
#### **--security-opt**=*option*
diff --git a/docs/source/markdown/podman-volume-ls.1.md b/docs/source/markdown/podman-volume-ls.1.md
index ab3813cca..489057446 100644
--- a/docs/source/markdown/podman-volume-ls.1.md
+++ b/docs/source/markdown/podman-volume-ls.1.md
@@ -16,7 +16,14 @@ flag. Use the **--quiet** flag to print only the volume names.
#### **--filter**=*filter*, **-f**
-Filter volume output.
+Volumes can be filtered by the following attributes:
+
+- dangling
+- driver
+- label
+- name
+- opt
+- scope
#### **--format**=*format*
diff --git a/hack/branch_commits.rb b/hack/branch_commits.rb
new file mode 100755
index 000000000..f13f8b2d0
--- /dev/null
+++ b/hack/branch_commits.rb
@@ -0,0 +1,98 @@
+#!/usr/bin/ruby
+
+require 'set'
+
+# Get commits in one branch, but not in another, accounting for cherry-picks.
+# Accepts two arguments: base branch and old branch. Commits in base branch that
+# are not in old branch will be reported.
+
+# Preface: I know exactly enough ruby to be dangerous with it.
+# For anyone reading this who is actually skilled at writing Ruby, I can only
+# say I'm very, very sorry.
+
+# Utility functions:
+
+# Check if a given Git branch exists
+def CheckBranchExists(branch)
+ return `git branch --list #{branch}`.rstrip.empty?
+end
+
+# Returns author (email) and commit subject for the given hash
+def GetCommitInfo(hash)
+ info = `git log -n 1 --format='%ae%n%s' #{hash}`.split("\n")
+ if info.length != 2
+ puts("Badly-formatted commit with hash #{hash}")
+ exit(127)
+ end
+ return info[0], info[1]
+end
+
+# Actual script begins here
+
+if ARGV.length != 2
+ puts("Must provide exactly 2 arguments, base branch and old branch")
+ exit(127)
+end
+
+# Both branches must exist
+ARGV.each do |branch|
+ if !CheckBranchExists(branch)
+ puts("Branch #{branch} does not exist")
+ exit(127)
+ end
+end
+
+base = ARGV[0]
+old = ARGV[1]
+
+# Get a base list of commits
+commits = `git log --no-merges --format=%H #{base} ^#{old}`.split("\n")
+
+# Alright, now for the hacky bit.
+# We want to remove every commit with a shortlog precisely matching something in
+# the old branch. This is an effort to catch cherry-picks, where commit ID has
+# almost certainly changed because the committer is different (and possibly
+# conflicts needed to be resolved).
+# We will match also try and match author, but not committer (which is reset to
+# whoever did the cherry-pick). We will *not* match full commit body - I
+# routinely edit these when I fix cherry-pick conflicts to indicate that I made
+# changes. A more ambitious future committer could attempt to see if the body of
+# the commit message in the old branch is a subset of the full commit message
+# from the base branch, but there are potential performance implications in that
+# due to the size of the string comparison that would be needed.
+# This will not catch commits where the shortlog is deliberately altered as part
+# of the cherry pick... But we can just ask folks not to do that, I guess?
+# (A classic example of something this wouldn't catch: cherry-picking a commit
+# to a branch and then prepending the branch name to the commit subject. I see
+# this a lot in Github PR subjects, but fortunately not much at all in actual
+# commit subjects).
+
+# Begin by fetching commit author + subject for each commit in old branch.
+# Map each author to an array of potential commit subjects.
+oldIndex = {}
+
+# TODO: This could probably be made a whole lot more efficient by unifying the
+# GetCommitInfo bits into two big `git log --format` calls.
+# But I'm not really ambitious enough to do that...
+oldCommits = `git log --no-merges --format=%H #{old}`.split("\n")
+oldCommits.each do |hash|
+ name, subject = GetCommitInfo(hash)
+ if oldIndex[name] == nil
+ oldIndex[name] = Set[]
+ end
+ oldIndex[name].add(subject)
+end
+
+# Go through our earlier commits list and check for matches.
+filtered = commits.reject do |hash|
+ name, subject = GetCommitInfo(hash)
+ oldIndex[name] != nil && oldIndex[name].include?(subject)
+end
+
+# We have now filtered out all commits we want to filter.
+# Now we just have to print all remaining commits.
+# This breaks the default pager, but we can just pipe to less.
+filtered.each do |hash|
+ puts `git log -n 1 #{hash}`
+ puts "\n"
+end
diff --git a/libpod/container.go b/libpod/container.go
index bad91d54f..591cf9bc5 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -237,6 +237,18 @@ type ContainerImageVolume struct {
ReadWrite bool `json:"rw"`
}
+// ContainerSecret is a secret that is mounted in a container
+type ContainerSecret struct {
+ // Secret is the secret
+ *secrets.Secret
+ // UID is tbe UID of the secret file
+ UID uint32
+ // GID is the GID of the secret file
+ GID uint32
+ // Mode is the mode of the secret file
+ Mode uint32
+}
+
// ContainerNetworkDescriptions describes the relationship between the CNI
// network and the ethN where N is an integer
type ContainerNetworkDescriptions map[string]int
@@ -1126,7 +1138,7 @@ func (c *Container) Umask() string {
}
//Secrets return the secrets in the container
-func (c *Container) Secrets() []*secrets.Secret {
+func (c *Container) Secrets() []*ContainerSecret {
return c.config.Secrets
}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 904c03f9b..0de79fde3 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -148,7 +148,7 @@ type ContainerRootFSConfig struct {
// default, but others do not.
CreateWorkingDir bool `json:"createWorkingDir,omitempty"`
// Secrets lists secrets to mount into the container
- Secrets []*secrets.Secret `json:"secrets,omitempty"`
+ Secrets []*ContainerSecret `json:"secrets,omitempty"`
// SecretPath is the secrets location in storage
SecretsPath string `json:"secretsPath"`
// Volatile specifies whether the container storage can be optimized
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 5b2103c92..4210bc581 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -343,11 +343,13 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
ctrConfig.CreateCommand = c.config.CreateCommand
ctrConfig.Timezone = c.config.Timezone
-
for _, secret := range c.config.Secrets {
newSec := define.InspectSecret{}
newSec.Name = secret.Name
newSec.ID = secret.ID
+ newSec.UID = secret.UID
+ newSec.GID = secret.GID
+ newSec.Mode = secret.Mode
ctrConfig.Secrets = append(ctrConfig.Secrets, &newSec)
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 53b85a466..f77825efd 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -15,7 +15,7 @@ import (
metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/containers/buildah/copier"
- "github.com/containers/common/pkg/secrets"
+ butil "github.com/containers/buildah/util"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/cgroups"
@@ -24,6 +24,7 @@ import (
"github.com/containers/podman/v3/pkg/hooks/exec"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/selinux"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
@@ -1530,6 +1531,16 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
}()
}
+ // If /etc/mtab does not exist in container image, then we need to
+ // create it, so that mount command within the container will work.
+ mtab := filepath.Join(mountPoint, "/etc/mtab")
+ if err := os.MkdirAll(filepath.Dir(mtab), 0755); err != nil {
+ return "", errors.Wrap(err, "error creating mtab directory")
+ }
+ if err = os.Symlink("/proc/mounts", mtab); err != nil && !os.IsExist(err) {
+ return "", err
+ }
+
// Request a mount of all named volumes
for _, v := range c.config.NamedVolumes {
vol, err := c.mountNamedVolume(v, mountPoint)
@@ -2235,21 +2246,31 @@ func (c *Container) hasNamespace(namespace spec.LinuxNamespaceType) bool {
}
// extractSecretToStorage copies a secret's data from the secrets manager to the container's static dir
-func (c *Container) extractSecretToCtrStorage(name string) error {
- manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
+func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error {
+ manager, err := c.runtime.SecretsManager()
if err != nil {
return err
}
- secr, data, err := manager.LookupSecretData(name)
+ _, data, err := manager.LookupSecretData(secr.Name)
if err != nil {
return err
}
secretFile := filepath.Join(c.config.SecretsPath, secr.Name)
+ hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), secr.UID, secr.GID)
+ if err != nil {
+ return errors.Wrap(err, "unable to extract secret")
+ }
err = ioutil.WriteFile(secretFile, data, 0644)
if err != nil {
return errors.Wrapf(err, "unable to create %s", secretFile)
}
+ if err := os.Lchown(secretFile, int(hostUID), int(hostGID)); err != nil {
+ return err
+ }
+ if err := os.Chmod(secretFile, os.FileMode(secr.Mode)); err != nil {
+ return err
+ }
if err := label.Relabel(secretFile, c.config.MountLabel, false); err != nil {
return err
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 17b894ce0..1b2f5a496 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -29,7 +29,6 @@ import (
"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/chown"
"github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/secrets"
"github.com/containers/common/pkg/subscriptions"
"github.com/containers/common/pkg/umask"
"github.com/containers/podman/v3/libpod/define"
@@ -359,6 +358,25 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, err
}
+ // Add named volumes
+ for _, namedVol := range c.config.NamedVolumes {
+ volume, err := c.runtime.GetVolume(namedVol.Name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID())
+ }
+ mountPoint, err := volume.MountPoint()
+ if err != nil {
+ return nil, err
+ }
+ volMount := spec.Mount{
+ Type: "bind",
+ Source: mountPoint,
+ Destination: namedVol.Dest,
+ Options: namedVol.Options,
+ }
+ g.AddMount(volMount)
+ }
+
// Check if the spec file mounts contain the options z, Z or U.
// If they have z or Z, relabel the source directory and then remove the option.
// If they have U, chown the source directory and them remove the option.
@@ -392,25 +410,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
g.SetProcessSelinuxLabel(c.ProcessLabel())
g.SetLinuxMountLabel(c.MountLabel())
- // Add named volumes
- for _, namedVol := range c.config.NamedVolumes {
- volume, err := c.runtime.GetVolume(namedVol.Name)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID())
- }
- mountPoint, err := volume.MountPoint()
- if err != nil {
- return nil, err
- }
- volMount := spec.Mount{
- Type: "bind",
- Source: mountPoint,
- Destination: namedVol.Dest,
- Options: namedVol.Options,
- }
- g.AddMount(volMount)
- }
-
// Add bind mounts to container
for dstPath, srcPath := range c.state.BindMounts {
newMount := spec.Mount{
@@ -759,7 +758,10 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
}
if len(c.config.EnvSecrets) > 0 {
- manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir())
+ manager, err := c.runtime.SecretsManager()
+ if err != nil {
+ return nil, err
+ }
if err != nil {
return nil, err
}
@@ -2392,7 +2394,7 @@ func (c *Container) createSecretMountDir() error {
oldUmask := umask.Set(0)
defer umask.Set(oldUmask)
- if err := os.MkdirAll(src, 0644); err != nil {
+ if err := os.MkdirAll(src, 0755); err != nil {
return err
}
if err := label.Relabel(src, c.config.MountLabel, false); err != nil {
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 5283946fa..af8ba6ecf 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -713,13 +713,16 @@ type DriverData struct {
Data map[string]string `json:"Data"`
}
-// InspectHostPort provides information on a port on the host that a container's
-// port is bound to.
+// InspectSecret contains information on secrets mounted inside the container
type InspectSecret struct {
- // IP on the host we are bound to. "" if not specified (binding to all
- // IPs).
+ // Name is the name of the secret
Name string `json:"Name"`
- // Port on the host we are bound to. No special formatting - just an
- // integer stuffed into a string.
+ // ID is the ID of the secret
ID string `json:"ID"`
+ // ID is the UID of the mounted secret file
+ UID uint32 `json:"UID"`
+ // ID is the GID of the mounted secret file
+ GID uint32 `json:"GID"`
+ // ID is the ID of the mode of the mounted secret file
+ Mode uint32 `json:"Mode"`
}
diff --git a/libpod/diff.go b/libpod/diff.go
index 6ce8d809a..c5a53478b 100644
--- a/libpod/diff.go
+++ b/libpod/diff.go
@@ -7,7 +7,7 @@ import (
"github.com/pkg/errors"
)
-var containerMounts = map[string]bool{
+var initInodes = map[string]bool{
"/dev": true,
"/etc/hostname": true,
"/etc/hosts": true,
@@ -17,6 +17,7 @@ var containerMounts = map[string]bool{
"/run/.containerenv": true,
"/run/secrets": true,
"/sys": true,
+ "/etc/mtab": true,
}
// GetDiff returns the differences between the two images, layers, or containers
@@ -36,7 +37,7 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
changes, err := r.store.Changes(fromLayer, toLayer)
if err == nil {
for _, c := range changes {
- if containerMounts[c.Path] {
+ if initInodes[c.Path] {
continue
}
rchanges = append(rchanges, c)
diff --git a/libpod/options.go b/libpod/options.go
index be26ced99..f942d264b 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1695,23 +1695,12 @@ func WithUmask(umask string) CtrCreateOption {
}
// WithSecrets adds secrets to the container
-func WithSecrets(secretNames []string) CtrCreateOption {
+func WithSecrets(containerSecrets []*ContainerSecret) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
- manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
- if err != nil {
- return err
- }
- for _, name := range secretNames {
- secr, err := manager.Lookup(name)
- if err != nil {
- return err
- }
- ctr.config.Secrets = append(ctr.config.Secrets, secr)
- }
-
+ ctr.config.Secrets = containerSecrets
return nil
}
}
@@ -1723,7 +1712,7 @@ func WithEnvSecrets(envSecrets map[string]string) CtrCreateOption {
if ctr.valid {
return define.ErrCtrFinalized
}
- manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir())
+ manager, err := ctr.runtime.SecretsManager()
if err != nil {
return err
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 80fe92b54..d0bdeb574 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/pkg/sysregistriesv2"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
@@ -103,6 +104,8 @@ type Runtime struct {
// noStore indicates whether we need to interact with a store or not
noStore bool
+ // secretsManager manages secrets
+ secretsManager *secrets.SecretsManager
}
// SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set.
@@ -1022,6 +1025,18 @@ func (r *Runtime) GetSecretsStorageDir() string {
return filepath.Join(r.store.GraphRoot(), "secrets")
}
+// SecretsManager returns the directory that the secrets manager should take
+func (r *Runtime) SecretsManager() (*secrets.SecretsManager, error) {
+ if r.secretsManager == nil {
+ manager, err := secrets.NewManager(r.GetSecretsStorageDir())
+ if err != nil {
+ return nil, err
+ }
+ r.secretsManager = manager
+ }
+ return r.secretsManager, nil
+}
+
func graphRootMounted() bool {
f, err := os.OpenFile("/run/.containerenv", os.O_RDONLY, os.ModePerm)
if err != nil {
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 7d31e392f..4e4b2a8ab 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -366,7 +366,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, err
}
for _, secr := range ctr.config.Secrets {
- err = ctr.extractSecretToCtrStorage(secr.Name)
+ err = ctr.extractSecretToCtrStorage(secr)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index a94c5f5c5..0ac9b5d8d 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -12,7 +12,6 @@ import (
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
- "github.com/containers/common/pkg/secrets"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/define"
@@ -161,7 +160,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
)
// Create the secret manager before hand
- secretsManager, err := secrets.NewManager(ic.Libpod.GetSecretsStorageDir())
+ secretsManager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/secrets.go b/pkg/domain/infra/abi/secrets.go
index 764f4a9dc..1e1cbc70f 100644
--- a/pkg/domain/infra/abi/secrets.go
+++ b/pkg/domain/infra/abi/secrets.go
@@ -6,7 +6,6 @@ import (
"io/ioutil"
"path/filepath"
- "github.com/containers/common/pkg/secrets"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
)
@@ -14,7 +13,7 @@ import (
func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader io.Reader, options entities.SecretCreateOptions) (*entities.SecretCreateReport, error) {
data, _ := ioutil.ReadAll(reader)
secretsPath := ic.Libpod.GetSecretsStorageDir()
- manager, err := secrets.NewManager(secretsPath)
+ manager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}
@@ -36,8 +35,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
}
func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string) ([]*entities.SecretInfoReport, []error, error) {
- secretsPath := ic.Libpod.GetSecretsStorageDir()
- manager, err := secrets.NewManager(secretsPath)
+ manager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, nil, err
}
@@ -71,8 +69,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
}
func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) {
- secretsPath := ic.Libpod.GetSecretsStorageDir()
- manager, err := secrets.NewManager(secretsPath)
+ manager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}
@@ -105,8 +102,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt
toRemove []string
reports = []*entities.SecretRmReport{}
)
- secretsPath := ic.Libpod.GetSecretsStorageDir()
- manager, err := secrets.NewManager(secretsPath)
+ manager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 7682367b7..a0f5cc7e6 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -400,7 +400,24 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
}
if len(s.Secrets) != 0 {
- options = append(options, libpod.WithSecrets(s.Secrets))
+ manager, err := rt.SecretsManager()
+ if err != nil {
+ return nil, err
+ }
+ var secrs []*libpod.ContainerSecret
+ for _, s := range s.Secrets {
+ secr, err := manager.Lookup(s.Source)
+ if err != nil {
+ return nil, err
+ }
+ secrs = append(secrs, &libpod.ContainerSecret{
+ Secret: secr,
+ UID: s.UID,
+ GID: s.GID,
+ Mode: s.Mode,
+ })
+ }
+ options = append(options, libpod.WithSecrets(secrs))
}
if len(s.EnvSecrets) != 0 {
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index 4e41061a5..054388384 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -250,27 +250,26 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
if !exists {
return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name)
}
+
+ dest, options, err := parseMountPath(volume.MountPath, volume.ReadOnly)
+ if err != nil {
+ return nil, err
+ }
+
switch volumeSource.Type {
case KubeVolumeTypeBindMount:
- if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
- return nil, errors.Wrapf(err, "error in parsing MountPath")
- }
mount := spec.Mount{
- Destination: volume.MountPath,
+ Destination: dest,
Source: volumeSource.Source,
Type: "bind",
- }
- if volume.ReadOnly {
- mount.Options = []string{"ro"}
+ Options: options,
}
s.Mounts = append(s.Mounts, mount)
case KubeVolumeTypeNamed:
namedVolume := specgen.NamedVolume{
- Dest: volume.MountPath,
- Name: volumeSource.Source,
- }
- if volume.ReadOnly {
- namedVolume.Options = []string{"ro"}
+ Dest: dest,
+ Name: volumeSource.Source,
+ Options: options,
}
s.Volumes = append(s.Volumes, &namedVolume)
default:
@@ -300,6 +299,25 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
return s, nil
}
+func parseMountPath(mountPath string, readOnly bool) (string, []string, error) {
+ options := []string{}
+ splitVol := strings.Split(mountPath, ":")
+ if len(splitVol) > 2 {
+ return "", options, errors.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath)
+ }
+ dest := splitVol[0]
+ if len(splitVol) > 1 {
+ options = strings.Split(splitVol[1], ",")
+ }
+ if err := parse.ValidateVolumeCtrDir(dest); err != nil {
+ return "", options, errors.Wrapf(err, "error in parsing MountPath")
+ }
+ if readOnly {
+ options = append(options, "ro")
+ }
+ return dest, options, nil
+}
+
func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container) {
if containerYAML.SecurityContext == nil {
return
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 2e01d1535..2815bdebb 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -258,7 +258,7 @@ type ContainerStorageConfig struct {
RootfsPropagation string `json:"rootfs_propagation,omitempty"`
// Secrets are the secrets that will be added to the container
// Optional.
- Secrets []string `json:"secrets,omitempty"`
+ Secrets []Secret `json:"secrets,omitempty"`
// Volatile specifies whether the container storage can be optimized
// at the cost of not syncing all the dirty files in memory.
Volatile bool `json:"volatile,omitempty"`
@@ -521,6 +521,13 @@ type PortMapping struct {
Protocol string `json:"protocol,omitempty"`
}
+type Secret struct {
+ Source string
+ UID uint32
+ GID uint32
+ Mode uint32
+}
+
var (
// ErrNoStaticIPRootless is used when a rootless user requests to assign a static IP address
// to a pod or container
diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go
index 6abe152a9..2886f06c1 100644
--- a/test/e2e/run_selinux_test.go
+++ b/test/e2e/run_selinux_test.go
@@ -343,4 +343,12 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(ContainSubstring("container_init_t"))
})
+
+ It("podman relabels named volume with :Z", func() {
+ session := podmanTest.Podman([]string{"run", "-v", "testvol:/test1/test:Z", fedoraMinimal, "ls", "-alZ", "/test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString(":s0:")
+ Expect(match).Should(BeTrue())
+ })
})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index f27ded5d2..58538b689 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1669,6 +1669,49 @@ WORKDIR /madethis`, BB)
Expect(session.OutputToString()).To(Equal(secretsString))
})
+ It("podman run --secret mount with uid, gid, mode options", func() {
+ secretsString := "somesecretdata"
+ secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
+ err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755)
+ Expect(err).To(BeNil())
+
+ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ // check default permissions
+ session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "secr", ALPINE, "ls", "-l", "/run/secrets/mysecret"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ output := session.OutputToString()
+ Expect(output).To(ContainSubstring("-r--r--r--"))
+ Expect(output).To(ContainSubstring("root"))
+
+ session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=mount,uid=1000,gid=1001,mode=777", "--name", "secr2", ALPINE, "ls", "-ln", "/run/secrets/mysecret"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ output = session.OutputToString()
+ Expect(output).To(ContainSubstring("-rwxrwxrwx"))
+ Expect(output).To(ContainSubstring("1000"))
+ Expect(output).To(ContainSubstring("1001"))
+ })
+
+ It("podman run --secret with --user", func() {
+ secretsString := "somesecretdata"
+ secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
+ err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755)
+ Expect(err).To(BeNil())
+
+ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "nonroot", "--user", "200:200", ALPINE, "cat", "/run/secrets/mysecret"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Equal(secretsString))
+ })
+
It("podman run invalid secret option", func() {
secretsString := "somesecretdata"
secretFilePath := filepath.Join(podmanTest.TempDir, "secret")
@@ -1694,6 +1737,11 @@ WORKDIR /madethis`, BB)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
+ // mount option with env type
+ session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=env,uid=1000", "--name", "secr", ALPINE, "printenv", "mysecret"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+
// No source given
session = podmanTest.Podman([]string{"run", "--secret", "type=env", "--name", "secr", ALPINE, "printenv", "mysecret"})
session.WaitWithDefaultTimeout()
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 9a136ff13..e12c32ef5 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -690,4 +690,18 @@ json-file | f
run_podman rm $cid
}
+@test "podman run no /etc/mtab " {
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+
+ cat >$tmpdir/Dockerfile <<EOF
+FROM $IMAGE
+RUN rm /etc/mtab
+EOF
+ expected="'/etc/mtab' -> '/proc/mounts'"
+ run_podman build -t nomtab $tmpdir
+ run_podman run --rm nomtab stat -c %N /etc/mtab
+ is "$output" "$expected" "/etc/mtab should be created"
+}
+
# vim: filetype=sh
diff --git a/test/system/700-play.bats b/test/system/700-play.bats
index 8fa96741c..bcd8cf939 100644
--- a/test/system/700-play.bats
+++ b/test/system/700-play.bats
@@ -51,18 +51,40 @@ spec:
seLinuxOptions:
level: "s0:c1,c2"
readOnlyRootFilesystem: false
+ volumeMounts:
+ - mountPath: /testdir:z
+ name: home-podman-testdir
workingDir: /
+ volumes:
+ - hostPath:
+ path: TESTDIR
+ type: Directory
+ name: home-podman-testdir
status: {}
"
+RELABEL="system_u:object_r:container_file_t:s0"
+
@test "podman play with stdin" {
- echo "$testYaml" > $PODMAN_TMPDIR/test.yaml
+ TESTDIR=$PODMAN_TMPDIR/testdir
+ mkdir -p $TESTDIR
+ echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman play kube - < $PODMAN_TMPDIR/test.yaml
+ if [ -e /usr/sbin/selinuxenabled -a /usr/sbin/selinuxenabled ]; then
+ run ls -Zd $TESTDIR
+ is "$output" ${RELABEL} "selinux relabel should have happened"
+ fi
run_podman pod rm -f test_pod
}
@test "podman play" {
- echo "$testYaml" > $PODMAN_TMPDIR/test.yaml
+ TESTDIR=$PODMAN_TMPDIR/testdir
+ mkdir -p $TESTDIR
+ echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman play kube $PODMAN_TMPDIR/test.yaml
+ if [ -e /usr/sbin/selinuxenabled -a /usr/sbin/selinuxenabled ]; then
+ run ls -Zd $TESTDIR
+ is "$output" ${RELABEL} "selinux relabel should have happened"
+ fi
run_podman pod rm -f test_pod
}