aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/images/build.go3
-rw-r--r--docs/source/markdown/podman-ps.1.md2
-rw-r--r--docs/source/markdown/podman-run.1.md16
-rw-r--r--docs/source/markdown/podman.1.md4
-rw-r--r--libpod/filters/containers.go31
-rw-r--r--libpod/options.go2
-rw-r--r--libpod/runtime_volume_linux.go2
-rw-r--r--pkg/api/server/register_containers.go1
-rw-r--r--pkg/domain/infra/abi/parse/parse.go6
-rw-r--r--pkg/specgen/generate/kube/kube.go20
-rw-r--r--test/e2e/play_kube_test.go49
-rw-r--r--test/e2e/ps_test.go51
-rw-r--r--test/e2e/volume_create_test.go8
-rw-r--r--test/system/070-build.bats17
14 files changed, 199 insertions, 13 deletions
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index fbea1e3d8..3aca104e3 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -221,7 +221,8 @@ func build(cmd *cobra.Command, args []string) error {
var logfile *os.File
if cmd.Flag("logfile").Changed {
- logfile, err := os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+ var err error
+ logfile, err = os.OpenFile(buildOpts.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return err
}
diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md
index b94964f6c..28212b92c 100644
--- a/docs/source/markdown/podman-ps.1.md
+++ b/docs/source/markdown/podman-ps.1.md
@@ -57,6 +57,8 @@ Valid filters are listed below:
| since | [ID] or [Name] Containers created since this container |
| volume | [VolumeName] or [MountpointDestination] Volume mounted in container |
| health | [Status] healthy or unhealthy |
+| pod | [Pod] name or full or partial ID of pod |
+
#### **--format**=*format*
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index cd45e53ef..2b4ea5571 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -394,6 +394,13 @@ Run the container in a new user namespace using the supplied mapping. This optio
This option can be passed several times to map different ranges. If calling **podman run** as an unprivileged user, the user needs to have the right to use the mapping. See **subuid**(5).
The example maps gids **0-1999** in the container to the gids **30000-31999** on the host: **--gidmap=0:30000:2000**.
+**Important note:** The new user namespace mapping based on **--gidmap** is based on the initial mapping made in the _/etc/subgid_ file.
+Assuming there is a _/etc/subgid_ mapping **groupname:100000:65536**, then **groupname** is initially mapped to a namespace starting with
+gid **100000** for **65536** ids. From here the **--gidmap** mapping to the new namespace starts from **0** again, but is based on the initial mapping.
+Meaning **groupname** is initially mapped to gid **100000** which is referenced as **0** in the following **--gidmap** mapping. In terms of the example
+above: The group **groupname** is mapped to group **100000** of the initial namespace then the
+**30000**st id of this namespace (which is gid 130000 in this namespace) is mapped to container namespace group id **0**. (groupname -> 100000 / 30000 -> 0)
+
#### **--group-add**=*group*
Add additional groups to run as
@@ -1026,6 +1033,15 @@ as an unprivileged user, the user needs to have the right to use the mapping. Se
The following example maps uids 0-1999 in the container to the uids 30000-31999 on the host: **--uidmap=0:30000:2000**.
+**Important note:** The new user namespace mapping based on **--uidmap** is based on the initial mapping made in the _/etc/subuid_ file.
+Assuming there is a _/etc/subuid_ mapping **username:100000:65536**, then **username** is initially mapped to a namespace starting with
+uid **100000** for **65536** ids. From here the **--uidmap** mapping to the new namespace starts from **0** again, but is based on the initial mapping.
+Meaning **username** is initially mapped to uid **100000** which is referenced as **0** in the following **--uidmap** mapping. In terms of the example
+above: The user **username** is mapped to user **100000** of the initial namespace then the
+**30000**st id of this namespace (which is uid 130000 in this namespace) is mapped to container namespace user id **0**. (username -> 100000 / 30000 -> 0)
+
+_Note_: A minimal mapping has to have at least container uid **0** mapped to the parent user namespace.
+
#### **--ulimit**=*option*
Ulimit options. You can use **host** to copy the current configuration from the host.
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index 7da01d389..79862b4d9 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -44,7 +44,9 @@ Path of the conmon binary (Default path is configured in `containers.conf`)
#### **--events-backend**=*type*
-Backend to use for storing events. Allowed values are **file**, **journald**, and **none**.
+Backend to use for storing events. Allowed values are **file**, **journald**, and
+**none**. When *file* is specified, the events are stored under a subdirectory
+of the *tmpdir* location (see **--tmpdir** below).
#### **--help**, **-h**
diff --git a/libpod/filters/containers.go b/libpod/filters/containers.go
index 2520c4f30..505429de6 100644
--- a/libpod/filters/containers.go
+++ b/libpod/filters/containers.go
@@ -203,6 +203,37 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
}
return false
}, nil
+ case "pod":
+ var pods []*libpod.Pod
+ for _, podNameOrID := range filterValues {
+ p, err := r.LookupPod(podNameOrID)
+ if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchPod {
+ continue
+ }
+ return nil, err
+ }
+ pods = append(pods, p)
+ }
+ return func(c *libpod.Container) bool {
+ // if no pods match, quick out
+ if len(pods) < 1 {
+ return false
+ }
+ // if the container has no pod id, quick out
+ if len(c.PodID()) < 1 {
+ return false
+ }
+ for _, p := range pods {
+ // we already looked up by name or id, so id match
+ // here is ok
+ if p.ID() == c.PodID() {
+ return true
+ }
+ }
+ return false
+ }, nil
+
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
diff --git a/libpod/options.go b/libpod/options.go
index c2db13560..1ef058cc8 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1593,7 +1593,7 @@ func WithVolumeOptions(options map[string]string) VolumeCreateOption {
volume.config.Options = make(map[string]string)
for key, value := range options {
switch key {
- case "type", "device", "o":
+ case "type", "device", "o", "UID", "GID":
volume.config.Options[key] = value
default:
return errors.Wrapf(define.ErrInvalidArg, "unrecognized volume option %q is not supported with local driver", key)
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index e1877b17d..9bf0fd108 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -58,7 +58,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
// Validate options
for key := range volume.config.Options {
switch key {
- case "device", "o", "type":
+ case "device", "o", "type", "UID", "GID":
// Do nothing, valid keys
default:
return nil, errors.Wrapf(define.ErrInvalidArg, "invalid mount option %s for driver 'local'", key)
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 870c6a90c..b80dea545 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -690,6 +690,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - `label`=(`key` or `"key=value"`) of an container label
// - `name=<name>` a container's name
// - `network`=(`<network id>` or `<network name>`)
+ // - `pod`=(`<pod id>` or `<pod name>`)
// - `publish`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`)
// - `since`=(`<container id>` or `<container name>`)
// - `status`=(`created`, `restarting`, `running`, `removing`, `paused`, `exited` or `dead`)
diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go
index 37568ea11..6a6380e33 100644
--- a/pkg/domain/infra/abi/parse/parse.go
+++ b/pkg/domain/infra/abi/parse/parse.go
@@ -38,6 +38,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error)
}
logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID)
libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID))
+ finalVal = append(finalVal, o)
+ // set option "UID": "$uid"
+ volumeOptions["UID"] = splitO[1]
case "gid":
if len(splitO) != 2 {
return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID")
@@ -48,6 +51,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error)
}
logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID)
libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID))
+ finalVal = append(finalVal, o)
+ // set option "GID": "$gid"
+ volumeOptions["GID"] = splitO[1]
default:
finalVal = append(finalVal, o)
}
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index 5cc7891ac..c64fe76a6 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -112,11 +112,18 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI
return nil, err
}
s.WorkDir = "/"
+ // We will use "Docker field name" internally here to avoid confusion
+ // and reference the "Kubernetes field name" when referencing the YAML
+ // ref: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes
+ entrypoint := []string{}
+ cmd := []string{}
if imageData != nil && imageData.Config != nil {
if imageData.Config.WorkingDir != "" {
s.WorkDir = imageData.Config.WorkingDir
}
- s.Command = imageData.Config.Entrypoint
+ // Pull entrypoint and cmd from image
+ entrypoint = imageData.Config.Entrypoint
+ cmd = imageData.Config.Cmd
s.Labels = imageData.Config.Labels
if len(imageData.Config.StopSignal) > 0 {
stopSignal, err := util.ParseSignal(imageData.Config.StopSignal)
@@ -126,13 +133,18 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI
s.StopSignal = &stopSignal
}
}
+ // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd
if len(containerYAML.Command) != 0 {
- s.Command = containerYAML.Command
+ entrypoint = containerYAML.Command
+ cmd = []string{}
}
- // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes
+ // Only override the cmd field if yaml.Args is specified
+ // Keep the image entrypoint, or the yaml.command if specified
if len(containerYAML.Args) != 0 {
- s.Command = append(s.Command, containerYAML.Args...)
+ cmd = containerYAML.Args
}
+
+ s.Command = append(entrypoint, cmd...)
// FIXME,
// we are currently ignoring imageData.Config.ExposedPorts
if containerYAML.WorkingDir != "" {
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 3a2387559..4fec56105 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -820,8 +820,28 @@ var _ = Describe("Podman play kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd))
})
+ // If you do not supply command or args for a Container, the defaults defined in the Docker image are used.
+ It("podman play kube test correct args and cmd when not specified", func() {
+ pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg(nil))))
+ err := generateKubeYaml("pod", 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))
+
+ // this image's ENTRYPOINT is `/entrypoint.sh` and it's COMMAND is `/etc/docker/registry/config.yml`
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh /etc/docker/registry/config.yml]`))
+ })
+
+ // If you supply a command but no args for a Container, only the supplied command is used.
+ // The default EntryPoint and the default Cmd defined in the Docker image are ignored.
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))))
+ pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo", "hello"}), withArg(nil))))
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
@@ -837,8 +857,9 @@ var _ = Describe("Podman play kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`))
})
+ // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied.
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"}))))
+ pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg([]string{"echo", "hello"}))))
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
@@ -849,9 +870,27 @@ var _ = Describe("Podman play kube", func() {
inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect.ExitCode()).To(Equal(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]`))
+ // this image's ENTRYPOINT is `/entrypoint.sh`
+ // so result should be `/entrypoint.sh + withArg(...)`
+ Expect(inspect.OutputToString()).To(ContainSubstring(`[/entrypoint.sh echo hello]`))
+ })
+
+ // If you supply a command and args,
+ // the default Entrypoint and the default Cmd defined in the Docker image are ignored.
+ // Your command is run with your args.
+ It("podman play kube test correct command with both set args and cmd in yaml file", func() {
+ pod := getPod(withCtr(getCtr(withImage(registry), withCmd([]string{"echo"}), withArg([]string{"hello"}))))
+ err := generateKubeYaml("pod", 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(`[echo hello]`))
})
It("podman play kube test correct output", func() {
diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go
index 05571157c..0c5d817ba 100644
--- a/test/e2e/ps_test.go
+++ b/test/e2e/ps_test.go
@@ -673,4 +673,55 @@ var _ = Describe("Podman ps", func() {
Expect(session.LineInOutputContains("test3")).To(BeTrue())
Expect(session.LineInOutputContains("test4")).To(BeTrue())
})
+ It("podman ps filter pod", func() {
+ pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"})
+ pod1.WaitWithDefaultTimeout()
+ Expect(pod1.ExitCode()).To(BeZero())
+ con1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", ALPINE, "top"})
+ con1.WaitWithDefaultTimeout()
+ Expect(con1.ExitCode()).To(BeZero())
+
+ pod2 := podmanTest.Podman([]string{"pod", "create", "--name", "pod2"})
+ pod2.WaitWithDefaultTimeout()
+ Expect(pod2.ExitCode()).To(BeZero())
+ con2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod2", ALPINE, "top"})
+ con2.WaitWithDefaultTimeout()
+ Expect(con2.ExitCode()).To(BeZero())
+
+ // bogus pod name or id should not result in error
+ session := podmanTest.Podman([]string{"ps", "--filter", "pod=1234"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+
+ // filter by pod name
+ session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ Expect(len(session.OutputToStringArray())).To(Equal(2))
+ Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray()))
+
+ // filter by full pod id
+ session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ Expect(len(session.OutputToStringArray())).To(Equal(2))
+ Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray()))
+
+ // filter by partial pod id
+ session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()[0:12]})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ Expect(len(session.OutputToStringArray())).To(Equal(2))
+ Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray()))
+
+ // filter by multiple pods is inclusive
+ session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1", "--filter", "pod=pod2"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ Expect(len(session.OutputToStringArray())).To(Equal(4))
+ Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray()))
+ Expect(StringInSlice(pod2.OutputToString(), session.OutputToStringArray()))
+
+ })
+
})
diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go
index 8c44e57e4..544532ee0 100644
--- a/test/e2e/volume_create_test.go
+++ b/test/e2e/volume_create_test.go
@@ -82,5 +82,13 @@ var _ = Describe("Podman volume create", func() {
inspectGID.WaitWithDefaultTimeout()
Expect(inspectGID.ExitCode()).To(Equal(0))
Expect(inspectGID.OutputToString()).To(Equal(gid))
+
+ // options should containt `uid=3000,gid=4000:3000:4000`
+ optionFormat := `{{ .Options.o }}:{{ .Options.UID }}:{{ .Options.GID }}`
+ optionStrFormatExpect := fmt.Sprintf(`uid=%s,gid=%s:%s:%s`, uid, gid, uid, gid)
+ inspectOpts := podmanTest.Podman([]string{"volume", "inspect", "--format", optionFormat, volName})
+ inspectOpts.WaitWithDefaultTimeout()
+ Expect(inspectOpts.ExitCode()).To(Equal(0))
+ Expect(inspectOpts.OutputToString()).To(Equal(optionStrFormatExpect))
})
})
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 8e9a2d613..048cf535d 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -424,6 +424,23 @@ EOF
run_podman rmi -a --force
}
+@test "podman build --logfile test" {
+ tmpdir=$PODMAN_TMPDIR/build-test
+ mkdir -p $tmpdir
+ tmpbuilddir=$tmpdir/build
+ mkdir -p $tmpbuilddir
+ dockerfile=$tmpbuilddir/Dockerfile
+ cat >$dockerfile <<EOF
+FROM $IMAGE
+EOF
+
+ run_podman build -t build_test --format=docker --logfile=$tmpdir/logfile $tmpbuilddir
+ run cat $tmpdir/logfile
+ is "$output" ".*STEP 2: COMMIT" "COMMIT seen in log"
+
+ run_podman rmi -f build_test
+}
+
function teardown() {
# A timeout or other error in 'build' can leave behind stale images
# that podman can't even see and which will cascade into subsequent