diff options
32 files changed, 355 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore index f0fdf4dc8..434a12759 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ /conmon/ contrib/spec/podman.spec *.coverprofile +coverprofile +/.coverage /docs/*.[158] /docs/*.[158].gz /docs/build/ @@ -2,6 +2,7 @@ export GO111MODULE=off export GOPROXY=https://proxy.golang.org GO ?= go +COVERAGE_PATH ?= .coverage DESTDIR ?= EPOCH_TEST_COMMIT ?= $(shell git merge-base $${DEST_BRANCH:-master} HEAD) HEAD ?= HEAD @@ -308,14 +309,20 @@ testunit: libpodimage ## Run unittest on the built image .PHONY: localunit localunit: test/goecho/goecho varlink_generate hack/check_root.sh make localunit + rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH} ginkgo \ -r \ $(TESTFLAGS) \ --skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings,hack \ --cover \ --covermode atomic \ + --coverprofile coverprofile \ + --outputdir ${COVERAGE_PATH} \ --tags "$(BUILDTAGS)" \ --succinct + $(GO) tool cover -html=${COVERAGE_PATH}/coverprofile -o ${COVERAGE_PATH}/coverage.html + $(GO) tool cover -func=${COVERAGE_PATH}/coverprofile > ${COVERAGE_PATH}/functions + cat ${COVERAGE_PATH}/functions | sed -n 's/\(total:\).*\([0-9][0-9].[0-9]\)/\1 \2/p' .PHONY: ginkgo ginkgo: diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 6ebf58204..46f78cdba 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -463,6 +463,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "tz", containerConfig.TZ(), "Set timezone in container", ) + createFlags.StringVar( + &cf.Umask, + "umask", containerConfig.Umask(), + "Set umask in container", + ) createFlags.StringSliceVar( &cf.UIDMap, "uidmap", []string{}, diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index a544846aa..2bea8b0b4 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -93,6 +93,7 @@ type ContainerCLIOpts struct { TmpFS []string TTY bool Timezone string + Umask string UIDMap []string Ulimit []string User string diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 416c6f6ec..731085731 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -613,6 +613,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.Remove = c.Rm s.StopTimeout = &c.StopTimeout s.Timezone = c.Timezone + s.Umask = c.Umask return nil } diff --git a/completions/bash/podman b/completions/bash/podman index 458090ac4..eb727ef63 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2119,12 +2119,13 @@ _podman_container_run() { --shm-size --stop-signal --stop-timeout - --tmpfs - --tz --subgidname --subuidname --sysctl --systemd + --tmpfs + --tz + --umask --uidmap --ulimit --user -u diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 03f23aec0..b4456225e 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -839,6 +839,10 @@ standard input. Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. +**--umask**=*umask* + +Set the umask inside the container. Defaults to `0022`. + **--uidmap**=*container_uid:host_uid:amount* UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags. diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 88f457bdf..303d025b0 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -880,6 +880,10 @@ standard input. Set timezone in container. This flag takes area-based timezones, GMT time, as well as `local`, which sets the timezone in the container to match the host machine. See `/usr/share/zoneinfo/` for valid timezones. +**--umask**=*umask* + +Set the umask inside the container. Defaults to `0022`. + **--uidmap**=*container_uid*:*host_uid*:*amount* Run the container in a new user namespace using the supplied mapping. This option conflicts @@ -11,7 +11,7 @@ require ( github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921 github.com/containernetworking/plugins v0.8.6 github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d - github.com/containers/common v0.16.0 + github.com/containers/common v0.17.0 github.com/containers/conmon v2.0.19+incompatible github.com/containers/image/v5 v5.5.1 github.com/containers/psgo v1.5.1 @@ -41,7 +41,7 @@ require ( github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/opencontainers/runc v1.0.0-rc91.0.20200708210054-ce54a9d4d79b github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 - github.com/opencontainers/runtime-tools v0.9.0 + github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa github.com/opencontainers/selinux v1.6.0 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 @@ -73,8 +73,8 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d h1:HgJJn1UBFjM464NpEmgLwVje5vSF/fBYAdLLoww9HgU= github.com/containers/buildah v1.15.1-0.20200708111410-d2ea9429455d/go.mod h1:HUAiD1mCGPFPcIuk5zls1LElLhXo7Q3hWDwheojjyAs= github.com/containers/common v0.15.2/go.mod h1:rhpXuGLTEKsk/xX/x0iKGHjRadMHpBd2ZiNDugwXPEM= -github.com/containers/common v0.16.0 h1:zAxDJ2tA2wBEjXwV/+ddC8s1f3MfqulH3waSjKxlX3o= -github.com/containers/common v0.16.0/go.mod h1:siKqOA03Bhh7Ss2m7fCsbVvbjwaNqrI+gZtC9FUp+DI= +github.com/containers/common v0.17.0 h1:3wkcSLw92UYPEhPSHeG1szyIe1qFgi9Jtj18l/tTii0= +github.com/containers/common v0.17.0/go.mod h1:bG22Wvr0iyWJ8UvIkL5dA5OEZFDqAJx1MejmWZb51HE= github.com/containers/conmon v2.0.19+incompatible h1:1bDVRvHy2MUNTUT/SW6LlHsJHQBTSwXvnKNdcB/a1vQ= github.com/containers/conmon v2.0.19+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.5.1 h1:h1FCOXH6Ux9/p/E4rndsQOC4yAdRU0msRTfLVeQ7FDQ= @@ -344,6 +344,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.m github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa h1:iyj+fFHVBn0xOalz9UChYzSU1K0HJ+d75b4YqShBRhI= +github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= diff --git a/libpod/container.go b/libpod/container.go index fda018640..8a69df685 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -437,6 +437,9 @@ type ContainerConfig struct { // Timezone is the timezone inside the container. // Local means it has the same timezone as the host machine Timezone string `json:"timezone,omitempty"` + + // Umask is the umask inside the container. + Umask string `json:"umask,omitempty"` } // ContainerNamedVolume is a named volume that will be mounted into the @@ -1276,5 +1279,8 @@ func (c *Container) AutoRemove() bool { func (c *Container) Timezone() string { return c.config.Timezone +} +func (c *Container) Umask() string { + return c.config.Umask } diff --git a/libpod/container_exec.go b/libpod/container_exec.go index bd04ee9b9..a16aea06d 100644 --- a/libpod/container_exec.go +++ b/libpod/container_exec.go @@ -729,10 +729,6 @@ func (c *Container) Exec(config *ExecConfig, streams *define.AttachStreams, resi return -1, err } - if exitCode != 0 { - return exitCode, errors.Wrapf(define.ErrOCIRuntime, "exec session exited with non-zero exit code %d", exitCode) - } - return exitCode, nil } diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 680776dba..a0d223c8c 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -325,6 +325,14 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp ctrConfig.Timezone = c.config.Timezone + // Pad Umask to 4 characters + if len(c.config.Umask) < 4 { + pad := strings.Repeat("0", 4-len(c.config.Umask)) + ctrConfig.Umask = pad + c.config.Umask + } else { + ctrConfig.Umask = c.config.Umask + } + return ctrConfig } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 1c21f2ff9..edea62a0d 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -355,6 +355,14 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { g.SetProcessGID(uint32(execUser.Gid)) } + if c.config.Umask != "" { + decVal, err := strconv.ParseUint(c.config.Umask, 8, 32) + if err != nil { + return nil, errors.Wrapf(err, "Invalid Umask Value") + } + g.SetProcessUmask(uint32(decVal)) + } + // Add addition groups if c.config.GroupAdd is not empty if len(c.config.Groups) > 0 { gids, err := lookup.GetContainerGroups(c.config.Groups, c.state.Mountpoint, overrides) diff --git a/libpod/define/config.go b/libpod/define/config.go index 64b24d9e2..6c426f2ec 100644 --- a/libpod/define/config.go +++ b/libpod/define/config.go @@ -20,6 +20,8 @@ var ( NameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$") // RegexError is thrown in presence of an invalid container/pod name. RegexError = errors.Wrapf(ErrInvalidArg, "names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*") + // UmaskRegex is a regular expression to validate Umask. + UmaskRegex = regexp.MustCompile(`^[0-7]{1,4}$`) ) const ( diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index fbd9da3e7..a08cb3de6 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -61,6 +61,8 @@ type InspectContainerConfig struct { // systemd mode, the container configuration is customized to optimize // running systemd in the container. SystemdMode bool `json:"SystemdMode,omitempty"` + // Umask is the umask inside the container. + Umask string `json:"Umask,omitempty"` } // InspectRestartPolicy holds information about the container's restart policy. diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index b04742974..4818f8dc4 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -92,7 +92,7 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { hcResult := define.HealthCheckSuccess config := new(ExecConfig) config.Command = newCommand - _, hcErr := c.Exec(config, streams, nil) + exitCode, hcErr := c.Exec(config, streams, nil) if hcErr != nil { errCause := errors.Cause(hcErr) hcResult = define.HealthCheckFailure @@ -104,6 +104,9 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { } else { returnCode = 125 } + } else if exitCode != 0 { + hcResult = define.HealthCheckFailure + returnCode = 1 } timeEnd := time.Now() if c.HealthCheckConfig().StartPeriod > 0 { diff --git a/libpod/options.go b/libpod/options.go index 40cf452db..41b0d7212 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1607,6 +1607,20 @@ func WithTimezone(path string) CtrCreateOption { } } +// WithUmask sets the umask in the container +func WithUmask(umask string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + if !define.UmaskRegex.MatchString(umask) { + return errors.Wrapf(define.ErrInvalidArg, "Invalid umask string %s", umask) + } + ctr.config.Umask = umask + return nil + } +} + // Pod Creation Options // WithPodName sets the name of the pod. diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index f82da2c95..888811958 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -453,11 +453,16 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container containerConfig.Command = []string{} if imageData != nil && imageData.Config != nil { - containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...) + containerConfig.Command = imageData.Config.Entrypoint } if len(containerYAML.Command) != 0 { - containerConfig.Command = append(containerConfig.Command, containerYAML.Command...) - } else if imageData != nil && imageData.Config != nil { + containerConfig.Command = containerYAML.Command + } + // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes + if len(containerYAML.Args) != 0 { + containerConfig.Command = append(containerConfig.Command, containerYAML.Args...) + } else if len(containerYAML.Command) == 0 { + // Add the Cmd from the image config only if containerYAML.Command and containerYAML.Args are empty containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...) } if imageData != nil && len(containerConfig.Command) == 0 { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 6dbc45c16..934d5fbac 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -145,6 +145,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if s.Timezone != "" { options = append(options, libpod.WithTimezone(s.Timezone)) } + if s.Umask != "" { + options = append(options, libpod.WithUmask(s.Umask)) + } useSystemd := false switch s.Systemd { diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index c6079be33..84a6c36a0 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -287,6 +287,8 @@ type ContainerSecurityConfig struct { // ReadOnlyFilesystem indicates that everything will be mounted // as read-only ReadOnlyFilesystem bool `json:"read_only_filesystem,omittempty"` + // Umask is the umask the init process of the container will be run with. + Umask string `json:"umask,omitempty"` } // ContainerCgroupConfig contains configuration information about a container's diff --git a/test/e2e/config/containers.conf b/test/e2e/config/containers.conf index 0a07676c4..5f852468d 100644 --- a/test/e2e/config/containers.conf +++ b/test/e2e/config/containers.conf @@ -50,3 +50,5 @@ dns_servers=[ "1.2.3.4", ] dns_options=[ "debug", ] tz = "Pacific/Honolulu" + +umask = "0002" diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 23d8dd197..aebbca855 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -218,6 +218,17 @@ var _ = Describe("Podman run", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("HST")) + }) + It("podman run containers.conf umask", func() { + //containers.conf umask set to 0002 + if !strings.Contains(podmanTest.OCIRuntime, "crun") { + Skip("Test only works on crun") + } + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0002")) }) + }) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index f21f17d39..09b4f5911 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" . "github.com/containers/libpod/v2/test/utils" . "github.com/onsi/ginkgo" @@ -499,4 +500,46 @@ var _ = Describe("Podman create", func() { Expect(data[0].Config.Timezone).To(Equal("local")) }) + It("podman create --umask", func() { + if !strings.Contains(podmanTest.OCIRuntime, "crun") { + Skip("Test only works on crun") + } + + session := podmanTest.Podman([]string{"create", "--name", "default", ALPINE}) + session.WaitWithDefaultTimeout() + inspect := podmanTest.Podman([]string{"inspect", "default"}) + inspect.WaitWithDefaultTimeout() + data := inspect.InspectContainerToJSON() + Expect(len(data)).To(Equal(1)) + Expect(data[0].Config.Umask).To(Equal("0022")) + + session = podmanTest.Podman([]string{"create", "--umask", "0002", "--name", "umask", ALPINE}) + session.WaitWithDefaultTimeout() + inspect = podmanTest.Podman([]string{"inspect", "umask"}) + inspect.WaitWithDefaultTimeout() + data = inspect.InspectContainerToJSON() + Expect(len(data)).To(Equal(1)) + Expect(data[0].Config.Umask).To(Equal("0002")) + + session = podmanTest.Podman([]string{"create", "--umask", "0077", "--name", "fedora", fedoraMinimal}) + session.WaitWithDefaultTimeout() + inspect = podmanTest.Podman([]string{"inspect", "fedora"}) + inspect.WaitWithDefaultTimeout() + data = inspect.InspectContainerToJSON() + Expect(len(data)).To(Equal(1)) + Expect(data[0].Config.Umask).To(Equal("0077")) + + session = podmanTest.Podman([]string{"create", "--umask", "22", "--name", "umask-short", ALPINE}) + session.WaitWithDefaultTimeout() + inspect = podmanTest.Podman([]string{"inspect", "umask-short"}) + inspect.WaitWithDefaultTimeout() + data = inspect.InspectContainerToJSON() + Expect(len(data)).To(Equal(1)) + Expect(data[0].Config.Umask).To(Equal("0022")) + + session = podmanTest.Podman([]string{"create", "--umask", "9999", "--name", "bad", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask")) + }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 23604f47d..4b68f6232 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "text/template" . "github.com/containers/libpod/v2/test/utils" @@ -50,6 +51,10 @@ spec: {{ range .Cmd }} - {{.}} {{ end }} + args: + {{ range .Arg }} + - {{.}} + {{ end }} env: - name: PATH value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @@ -129,6 +134,10 @@ spec: {{ range .Cmd }} - {{.}} {{ end }} + args: + {{ range .Arg }} + - {{.}} + {{ end }} env: - name: PATH value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @@ -171,6 +180,7 @@ spec: var ( defaultCtrName = "testCtr" defaultCtrCmd = []string{"top"} + defaultCtrArg = []string{"-d", "1.5"} defaultCtrImage = ALPINE defaultPodName = "testPod" defaultDeploymentName = "testDeployment" @@ -322,6 +332,7 @@ type Ctr struct { Name string Image string Cmd []string + Arg []string SecurityContext bool Caps bool CapAdd []string @@ -332,7 +343,7 @@ type Ctr struct { // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults // and the configured options func getCtr(options ...ctrOption) *Ctr { - c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, true, false, nil, nil, ""} + c := Ctr{defaultCtrName, defaultCtrImage, defaultCtrCmd, defaultCtrArg, true, false, nil, nil, ""} for _, option := range options { option(&c) } @@ -347,6 +358,12 @@ func withCmd(cmd []string) ctrOption { } } +func withArg(arg []string) ctrOption { + return func(c *Ctr) { + c.Arg = arg + } +} + func withImage(img string) ctrOption { return func(c *Ctr) { c.Image = img @@ -438,14 +455,50 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).To(Equal(0)) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + // Use the defined command to override the image's command + correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") + Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) + }) + + It("podman play kube test correct command with only set command in yaml file", func() { + pod := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg(nil)))) + err := generatePodKubeYaml(pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + // Use the defined command to override the image's command, and don't set the args + // so the full command in result should not contains the image's command + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) + }) + + It("podman play kube test correct command with only set args in yaml file", func() { + pod := getPod(withCtr(getCtr(withImage(redis), withCmd(nil), withArg([]string{"echo", "hello"})))) + err := generatePodKubeYaml(pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0])) + // this image's ENTRYPOINT is called `docker-entrypoint.sh` + // so result should be `docker-entrypoint.sh + withArg(...)` + Expect(inspect.OutputToString()).To(ContainSubstring(`[docker-entrypoint.sh echo hello]`)) }) It("podman play kube test correct output", func() { - p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"})))) + p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg([]string{"world"})))) err := generatePodKubeYaml(p, kubeYaml) Expect(err).To(BeNil()) @@ -457,12 +510,12 @@ var _ = Describe("Podman generate kube", func() { logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(p)}) logs.WaitWithDefaultTimeout() Expect(logs.ExitCode()).To(Equal(0)) - Expect(logs.OutputToString()).To(ContainSubstring("hello")) + Expect(logs.OutputToString()).To(ContainSubstring("hello world")) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(p), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring("hello")) + Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello world]`)) }) It("podman play kube test hostname", func() { @@ -498,7 +551,7 @@ var _ = Describe("Podman generate kube", func() { It("podman play kube cap add", func() { capAdd := "CAP_SYS_ADMIN" - ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"})) + ctr := getCtr(withCapAdd([]string{capAdd}), withCmd([]string{"cat", "/proc/self/status"}), withArg(nil)) pod := getPod(withCtr(ctr)) err := generatePodKubeYaml(pod, kubeYaml) @@ -556,7 +609,7 @@ var _ = Describe("Podman generate kube", func() { } ctrAnnotation := "container.seccomp.security.alpha.kubernetes.io/" + defaultCtrName - ctr := getCtr(withCmd([]string{"pwd"})) + ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil)) pod := getPod(withCtr(ctr), withAnnotation(ctrAnnotation, "localhost/"+filepath.Base(jsonFile))) err = generatePodKubeYaml(pod, kubeYaml) @@ -582,7 +635,7 @@ var _ = Describe("Podman generate kube", func() { } defer os.Remove(jsonFile) - ctr := getCtr(withCmd([]string{"pwd"})) + ctr := getCtr(withCmd([]string{"pwd"}), withArg(nil)) pod := getPod(withCtr(ctr), withAnnotation("seccomp.security.alpha.kubernetes.io/pod", "localhost/"+filepath.Base(jsonFile))) err = generatePodKubeYaml(pod, kubeYaml) @@ -734,10 +787,12 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0])}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0])) + // yaml's command shuold override the image's Entrypoint + correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") + Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) }) It("podman play kube deployment more than 1 replica test correct command", func() { @@ -752,11 +807,12 @@ spec: Expect(kube.ExitCode()).To(Equal(0)) podNames := getPodNamesInDeployment(deployment) + correctCmd := "[" + strings.Join(defaultCtrCmd, " ") + " " + strings.Join(defaultCtrArg, " ") for i = 0; i < numReplicas; i++ { - inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i])}) + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() Expect(inspect.ExitCode()).To(Equal(0)) - Expect(inspect.OutputToString()).To(ContainSubstring(defaultCtrCmd[0])) + Expect(inspect.OutputToString()).To(ContainSubstring(correctCmd)) } }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 9d48f1540..7bb474769 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1081,4 +1081,35 @@ USER mail` Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(limit)) }) + + It("podman run umask", func() { + if !strings.Contains(podmanTest.OCIRuntime, "crun") { + Skip("Test only works on crun") + } + + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0022")) + + session = podmanTest.Podman([]string{"run", "--umask", "0002", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0002")) + + session = podmanTest.Podman([]string{"run", "--umask", "0077", "--rm", fedoraMinimal, "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0077")) + + session = podmanTest.Podman([]string{"run", "--umask", "22", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(Equal("0022")) + + session = podmanTest.Podman([]string{"run", "--umask", "9999", "--rm", ALPINE, "sh", "-c", "umask"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask")) + }) }) diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index b3278bb28..80c478505 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -622,9 +622,17 @@ func (c *ContainersConfig) Validate() error { // execution checks. It returns an `error` on validation failure, otherwise // `nil`. func (c *NetworkConfig) Validate() error { - if c.NetworkConfigDir != _cniConfigDir { - err := isDirectory(c.NetworkConfigDir) + expectedConfigDir := _cniConfigDir + if unshare.IsRootless() { + home, err := unshare.HomeDir() if err != nil { + return err + } + expectedConfigDir = filepath.Join(home, _cniConfigDirRootless) + } + if c.NetworkConfigDir != expectedConfigDir { + err := isDirectory(c.NetworkConfigDir) + if err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "invalid network_config_dir: %s", c.NetworkConfigDir) } } @@ -874,6 +882,10 @@ func Default() (*Config, error) { if config != nil || configErr != nil { return config, configErr } + return defConfig() +} + +func defConfig() (*Config, error) { config, configErr = NewConfig("") return config, configErr } @@ -969,13 +981,11 @@ func (c *Config) Write() error { // the cached containers.conf files. func Reload() (*Config, error) { configMutex.Lock() - configErr = nil - config = nil - configMutex.Unlock() - return Default() + defer configMutex.Unlock() + return defConfig() } -func (c *Config) ActiveDestination() (string, string, error){ +func (c *Config) ActiveDestination() (string, string, error) { if uri, found := os.LookupEnv("CONTAINER_HOST"); found { var ident string if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found { diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go index 12cf1b421..57b703f53 100644 --- a/vendor/github.com/containers/common/pkg/config/default.go +++ b/vendor/github.com/containers/common/pkg/config/default.go @@ -92,8 +92,10 @@ const ( // InstallPrefix is the prefix where podman will be installed. // It can be overridden at build time. _installPrefix = "/usr" - // _cniConfigDir is the directory where cni plugins are found + // _cniConfigDir is the directory where cni configuration is found _cniConfigDir = "/etc/cni/net.d/" + // _cniConfigDirRootless is the directory where cni plugins are found + _cniConfigDirRootless = ".config/cni/net.d/" // CgroupfsCgroupsManager represents cgroupfs native cgroup manager CgroupfsCgroupsManager = "cgroupfs" // DefaultApparmorProfile specifies the default apparmor profile for the container. @@ -138,6 +140,8 @@ func DefaultConfig() (*Config, error) { netns := "bridge" + cniConfig := _cniConfigDir + defaultEngineConfig.SignaturePolicyPath = DefaultSignaturePolicyPath if unshare.IsRootless() { home, err := unshare.HomeDir() @@ -152,6 +156,7 @@ func DefaultConfig() (*Config, error) { } } netns = "slirp4netns" + cniConfig = filepath.Join(home, _cniConfigDirRootless) } cgroupNS := "host" @@ -198,7 +203,7 @@ func DefaultConfig() (*Config, error) { }, Network: NetworkConfig{ DefaultNetwork: "podman", - NetworkConfigDir: _cniConfigDir, + NetworkConfigDir: cniConfig, CNIPluginDirs: cniBinDir, }, Engine: *defaultEngineConfig, diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index b75bbe971..2913fe974 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.16.0" +const Version = "0.17.0" diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go index 6d3268902..c757c20e0 100644 --- a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go +++ b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go @@ -29,6 +29,9 @@ var ( type Generator struct { Config *rspec.Spec HostSpecific bool + // This is used to keep a cache of the ENVs added to improve + // performance when adding a huge number of ENV variables + envMap map[string]int } // ExportOptions have toggles for exporting only certain parts of the specification @@ -236,7 +239,12 @@ func New(os string) (generator Generator, err error) { } } - return Generator{Config: &config}, nil + envCache := map[string]int{} + if config.Process != nil { + envCache = createEnvCacheMap(config.Process.Env) + } + + return Generator{Config: &config, envMap: envCache}, nil } // NewFromSpec creates a configuration Generator from a given @@ -246,8 +254,14 @@ func New(os string) (generator Generator, err error) { // // generator := Generator{Config: config} func NewFromSpec(config *rspec.Spec) Generator { + envCache := map[string]int{} + if config != nil && config.Process != nil { + envCache = createEnvCacheMap(config.Process.Env) + } + return Generator{ Config: config, + envMap: envCache, } } @@ -273,11 +287,27 @@ func NewFromTemplate(r io.Reader) (Generator, error) { if err := json.NewDecoder(r).Decode(&config); err != nil { return Generator{}, err } + + envCache := map[string]int{} + if config.Process != nil { + envCache = createEnvCacheMap(config.Process.Env) + } + return Generator{ Config: &config, + envMap: envCache, }, nil } +// createEnvCacheMap creates a hash map with the ENV variables given by the config +func createEnvCacheMap(env []string) map[string]int { + envMap := make(map[string]int, len(env)) + for i, val := range env { + envMap[val] = i + } + return envMap +} + // SetSpec sets the configuration in the Generator g. // // Deprecated: Replace with: @@ -414,6 +444,12 @@ func (g *Generator) SetProcessUsername(username string) { g.Config.Process.User.Username = username } +// SetProcessUmask sets g.Config.Process.User.Umask. +func (g *Generator) SetProcessUmask(umask uint32) { + g.initConfigProcess() + g.Config.Process.User.Umask = umask +} + // SetProcessGID sets g.Config.Process.User.GID. func (g *Generator) SetProcessGID(gid uint32) { g.initConfigProcess() @@ -456,21 +492,44 @@ func (g *Generator) ClearProcessEnv() { return } g.Config.Process.Env = []string{} + // Clear out the env cache map as well + g.envMap = map[string]int{} } // AddProcessEnv adds name=value into g.Config.Process.Env, or replaces an // existing entry with the given name. func (g *Generator) AddProcessEnv(name, value string) { + if name == "" { + return + } + g.initConfigProcess() + g.addEnv(fmt.Sprintf("%s=%s", name, value), name) +} - env := fmt.Sprintf("%s=%s", name, value) - for idx := range g.Config.Process.Env { - if strings.HasPrefix(g.Config.Process.Env[idx], name+"=") { - g.Config.Process.Env[idx] = env - return - } +// AddMultipleProcessEnv adds multiple name=value into g.Config.Process.Env, or replaces +// existing entries with the given name. +func (g *Generator) AddMultipleProcessEnv(envs []string) { + g.initConfigProcess() + + for _, val := range envs { + split := strings.SplitN(val, "=", 2) + g.addEnv(val, split[0]) + } +} + +// addEnv looks through adds ENV to the Process and checks envMap for +// any duplicates +// This is called by both AddMultipleProcessEnv and AddProcessEnv +func (g *Generator) addEnv(env, key string) { + if idx, ok := g.envMap[key]; ok { + // The ENV exists in the cache, so change its value in g.Config.Process.Env + g.Config.Process.Env[idx] = env + } else { + // else the env doesn't exist, so add it and add it's index to g.envMap + g.Config.Process.Env = append(g.Config.Process.Env, env) + g.envMap[key] = len(g.Config.Process.Env) - 1 } - g.Config.Process.Env = append(g.Config.Process.Env, env) } // AddProcessRlimits adds rlimit into g.Config.Process.Rlimits. @@ -1443,7 +1502,7 @@ func (g *Generator) AddDevice(device rspec.LinuxDevice) { return } if dev.Type == device.Type && dev.Major == device.Major && dev.Minor == device.Minor { - fmt.Fprintln(os.Stderr, "WARNING: The same type, major and minor should not be used for multiple devices.") + fmt.Fprintf(os.Stderr, "WARNING: Creating device %q with same type, major and minor as existing %q.\n", device.Path, dev.Path) } } diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go index 5fee5a3b2..8a8dc3970 100644 --- a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go +++ b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go @@ -566,6 +566,20 @@ func DefaultProfile(rs *specs.Spec) *rspec.LinuxSeccomp { }, }...) /* Flags parameter of the clone syscall is the 2nd on s390 */ + syscalls = append(syscalls, []rspec.LinuxSyscall{ + { + Names: []string{"clone"}, + Action: rspec.ActAllow, + Args: []rspec.LinuxSeccompArg{ + { + Index: 1, + Value: 2080505856, + ValueTwo: 0, + Op: rspec.OpMaskedEqual, + }, + }, + }, + }...) } return &rspec.LinuxSeccomp{ diff --git a/vendor/modules.txt b/vendor/modules.txt index 6038487cc..1d97f810a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,7 +84,7 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.16.0 +# github.com/containers/common v0.17.0 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/auth github.com/containers/common/pkg/capabilities @@ -421,7 +421,7 @@ github.com/opencontainers/runc/libcontainer/user github.com/opencontainers/runc/libcontainer/utils # github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 github.com/opencontainers/runtime-spec/specs-go -# github.com/opencontainers/runtime-tools v0.9.0 +# github.com/opencontainers/runtime-tools v0.9.1-0.20200714183735-07406c5828aa github.com/opencontainers/runtime-tools/error github.com/opencontainers/runtime-tools/filepath github.com/opencontainers/runtime-tools/generate |