diff --git a/libpod/container.go b/libpod/container.go index 9ad938a5c1..644647bc92 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -261,6 +261,10 @@ type ContainerConfig struct { Mounts []string `json:"mounts,omitempty"` // NamedVolumes lists the named volumes to mount into the container. NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"` + // CreateWorkingDir indicates that Libpod should create the container's + // working directory if it does not exist. Some OCI runtimes do this by + // default, but others do not. + CreateWorkingDir bool `json:"createWorkingDir,omitempty"` // Security Config diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index b9e4f9a935..0d9a1c8241 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -157,7 +157,32 @@ func (c *Container) prepare() error { } // Save changes to container state - return c.save() + if err := c.save(); err != nil { + return err + } + + // Ensure container entrypoint is created (if required) + if c.config.CreateWorkingDir { + workdir, err := securejoin.SecureJoin(c.state.Mountpoint, c.WorkingDir()) + if err != nil { + return errors.Wrapf(err, "error creating path to container %s working dir", c.ID()) + } + rootUID := c.RootUID() + rootGID := c.RootGID() + + if err := os.MkdirAll(workdir, 0755); err != nil { + if os.IsExist(err) { + return nil + } + return errors.Wrapf(err, "error creating container %s working dir", c.ID()) + } + + if err := os.Chown(workdir, rootUID, rootGID); err != nil { + return errors.Wrapf(err, "error chowning container %s working directory to container root", c.ID()) + } + } + + return nil } // cleanupNetwork unmounts and cleans up the container's network diff --git a/libpod/options.go b/libpod/options.go index 560b406e29..a4e4b99e9e 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1395,6 +1395,19 @@ func WithCreateCommand(cmd []string) CtrCreateOption { } } +// WithCreateWorkingDir tells Podman to create the container's working directory +// if it does not exist. +func WithCreateWorkingDir() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.CreateWorkingDir = true + return nil + } +} + // Volume Creation Options // WithVolumeName sets the name of the volume. diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index b31bc91e02..42be5e812f 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -215,6 +215,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if s.Entrypoint != nil { options = append(options, libpod.WithEntrypoint(s.Entrypoint)) } + // If the user did not set an workdir but the image did, ensure it is + // created. + if s.WorkDir == "" && img != nil { + newWD, err := img.WorkingDir(ctx) + if err != nil { + return nil, err + } + if newWD != "" { + options = append(options, libpod.WithCreateWorkingDir()) + } + } if s.StopSignal != nil { options = append(options, libpod.WithStopSignal(*s.StopSignal)) } diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 13f0db550a..ef275b32e1 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1060,4 +1060,14 @@ USER mail` Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(limit)) }) + + It("podman run makes entrypoint from image", func() { + dockerfile := `FROM busybox +WORKDIR /madethis` + podmanTest.BuildImage(dockerfile, "test", "false") + session := podmanTest.Podman([]string{"run", "--rm", "test", "pwd"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("/madethis")) + }) })