diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podman/build.go | 10 | ||||
-rw-r--r-- | cmd/podman/commit.go | 2 | ||||
-rw-r--r-- | cmd/podman/common.go | 6 | ||||
-rw-r--r-- | cmd/podman/create.go | 134 | ||||
-rw-r--r-- | cmd/podman/create_cli.go | 90 | ||||
-rw-r--r-- | cmd/podman/libpodruntime/runtime.go | 20 | ||||
-rw-r--r-- | cmd/podman/main.go | 2 | ||||
-rw-r--r-- | cmd/podman/run.go | 110 | ||||
-rw-r--r-- | cmd/podman/wait.go | 15 |
9 files changed, 221 insertions, 168 deletions
diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 0ca0b3d83..1b8a5faec 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -1,13 +1,13 @@ package main import ( + "github.com/containers/buildah" + "github.com/containers/buildah/imagebuildah" + buildahcli "github.com/containers/buildah/pkg/cli" + "github.com/containers/buildah/pkg/parse" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" - "github.com/projectatomic/buildah" - "github.com/projectatomic/buildah/imagebuildah" - buildahcli "github.com/projectatomic/buildah/pkg/cli" - "github.com/projectatomic/buildah/pkg/parse" "github.com/sirupsen/logrus" "github.com/urfave/cli" "io/ioutil" @@ -50,7 +50,7 @@ func getDockerfiles(files []string) []string { } func buildCmd(c *cli.Context) error { - // The following was taken directly from projectatomic/buildah/cmd/bud.go + // The following was taken directly from containers/buildah/cmd/bud.go // TODO Find a away to vendor more of this in rather than copy from bud output := "" diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 517eefd07..234926de0 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -6,13 +6,13 @@ import ( "os" "strings" + "github.com/containers/buildah" "github.com/containers/image/manifest" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" - "github.com/projectatomic/buildah" "github.com/urfave/cli" ) diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 578de7f38..9ab0e57e5 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -8,11 +8,11 @@ import ( "regexp" "strings" + "github.com/containers/buildah" "github.com/containers/libpod/libpod" "github.com/containers/storage" "github.com/fatih/camelcase" "github.com/pkg/errors" - "github.com/projectatomic/buildah" "github.com/urfave/cli" ) @@ -418,6 +418,10 @@ var createFlags = []cli.Flag{ Usage: "UTS namespace to use", }, cli.StringSliceFlag{ + Name: "mount", + Usage: "Attach a filesystem mount to the container (default [])", + }, + cli.StringSliceFlag{ Name: "volume, v", Usage: "Bind mount a volume into the container (default [])", }, diff --git a/cmd/podman/create.go b/cmd/podman/create.go index bc010d047..fc0c71536 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" "github.com/docker/go-units" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -57,6 +58,30 @@ var createCommand = cli.Command{ } func createCmd(c *cli.Context) error { + if err := createInit(c); err != nil { + return err + } + + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } + + runtime, err := libpodruntime.GetContainerRuntime(c) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + defer runtime.Shutdown(false) + + ctr, _, err := createContainer(c, runtime) + if err != nil { + return err + } + + fmt.Printf("%s\n", ctr.ID()) + return nil +} + +func createInit(c *cli.Context) error { // TODO should allow user to create based off a directory on the host not just image // Need CLI support for this @@ -83,63 +108,51 @@ func createCmd(c *cli.Context) error { return errors.Errorf("image name or ID is required") } + return nil +} + +func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) { + rtc := runtime.GetConfig() + ctx := getContext() rootfs := "" if c.Bool("rootfs") { rootfs = c.Args()[0] } - mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap")) - if err != nil { - return err - } - storageOpts, err := libpodruntime.GetDefaultStoreOptions() - if err != nil { - return err - } - storageOpts.UIDMap = mappings.UIDMap - storageOpts.GIDMap = mappings.GIDMap - - if os.Geteuid() != 0 { - rootless.SetSkipStorageSetup(true) - } - - runtime, err := libpodruntime.GetRuntimeWithStorageOpts(c, &storageOpts) - if err != nil { - return errors.Wrapf(err, "error creating libpod runtime") - } - defer runtime.Shutdown(false) - - rtc := runtime.GetConfig() - ctx := getContext() - imageName := "" var data *inspect.ImageData = nil + if rootfs == "" && !rootless.SkipStorageSetup() { newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false) if err != nil { - return err + return nil, nil, err } data, err = newImage.Inspect(ctx) - imageName = newImage.Names()[0] + names := newImage.Names() + if len(names) > 0 { + imageName = names[0] + } else { + imageName = newImage.ID() + } } createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data) if err != nil { - return err + return nil, nil, err } runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig) if err != nil { - return err + return nil, nil, err } options, err := createConfig.GetContainerCreateOptions(runtime) if err != nil { - return err + return nil, nil, err } became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, runtime) if err != nil { - return err + return nil, nil, err } if became { os.Exit(ret) @@ -147,27 +160,25 @@ func createCmd(c *cli.Context) error { ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...) if err != nil { - return err + return nil, nil, err } createConfigJSON, err := json.Marshal(createConfig) if err != nil { - return err + return nil, nil, err } if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil { - return err + return nil, nil, err } - logrus.Debug("new container created ", ctr.ID()) - if c.String("cidfile") != "" { err := libpod.WriteFile(ctr.ID(), c.String("cidfile")) if err != nil { logrus.Error(err) } } - fmt.Printf("%s\n", ctr.ID()) - return nil + logrus.Debugf("New container created %q", ctr.ID()) + return ctr, createConfig, nil } // Checks if a user-specified AppArmor profile is loaded, or loads the default profile if @@ -311,7 +322,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { } } } - config.ProcessLabel, config.MountLabel, err = label.InitLabels(labelOpts) + config.LabelOpts = labelOpts return err } @@ -449,6 +460,10 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } blkioWeight = uint16(u) } + var mountList []spec.Mount + if mountList, err = parseMounts(c.StringSlice("mount")); err != nil { + return nil, err + } if err = parseVolumes(c.StringSlice("volume")); err != nil { return nil, err @@ -518,17 +533,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } } - shmDir := "" - if ipcMode.IsHost() { - shmDir = "/dev/shm" - } else if ipcMode.IsContainer() { - ctr, err := runtime.LookupContainer(ipcMode.Container()) - if err != nil { - return nil, errors.Wrapf(err, "container %q not found", ipcMode.Container()) - } - shmDir = ctr.ShmDir() - } - // USER user := c.String("user") if user == "" { @@ -629,7 +633,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim command = append(command, data.ContainerConfig.Cmd...) } - if len(command) == 0 { + if data != nil && len(command) == 0 { return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image") } @@ -681,7 +685,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim } var systemd bool - if c.BoolT("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) { + if command != nil && c.BoolT("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) { systemd = true if signalString == "" { stopSignal, err = signal.ParseSignal("RTMIN+3") @@ -765,7 +769,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim Ulimit: c.StringSlice("ulimit"), }, Rm: c.Bool("rm"), - ShmDir: shmDir, StopSignal: stopSignal, StopTimeout: c.Uint("stop-timeout"), Sysctl: sysctl, @@ -774,6 +777,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim Tty: tty, User: user, UsernsMode: usernsMode, + Mounts: mountList, Volumes: c.StringSlice("volume"), WorkDir: workDir, Rootfs: rootfs, @@ -796,6 +800,11 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return config, nil } +type namespace interface { + IsContainer() bool + Container() string +} + func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *libpod.Runtime) (bool, int, error) { if os.Geteuid() == 0 { return false, 0, nil @@ -827,5 +836,26 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l } } + namespacesStr := []string{string(createConfig.IpcMode), string(createConfig.NetMode), string(createConfig.UsernsMode), string(createConfig.PidMode), string(createConfig.UtsMode)} + for _, i := range namespacesStr { + if cc.IsNS(i) { + return rootless.JoinNSPath(cc.NS(i)) + } + } + + namespaces := []namespace{createConfig.IpcMode, createConfig.NetMode, createConfig.UsernsMode, createConfig.PidMode, createConfig.UtsMode} + for _, i := range namespaces { + if i.IsContainer() { + ctr, err := runtime.LookupContainer(i.Container()) + if err != nil { + return false, -1, err + } + pid, err := ctr.PID() + if err != nil { + return false, -1, err + } + return rootless.JoinNS(uint(pid)) + } + } return rootless.BecomeRootInUserNS() } diff --git a/cmd/podman/create_cli.go b/cmd/podman/create_cli.go index 812b62058..218e9b806 100644 --- a/cmd/podman/create_cli.go +++ b/cmd/podman/create_cli.go @@ -8,6 +8,8 @@ import ( cc "github.com/containers/libpod/pkg/spec" "github.com/docker/docker/pkg/sysinfo" + "github.com/docker/go-units" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -74,6 +76,94 @@ func addWarning(warnings []string, msg string) []string { return append(warnings, msg) } +// Format supported. +// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... +// podman run --mount type=tmpfs,target=/dev/shm .. +func parseMounts(mounts []string) ([]spec.Mount, error) { + var mountList []spec.Mount + errInvalidSyntax := errors.Errorf("incorrect mount format : should be --mount type=<bind|tmpfs>,[src=<host-dir>,]target=<ctr-dir>,[options]") + for _, mount := range mounts { + var tokenCount int + var mountInfo spec.Mount + + arr := strings.SplitN(mount, ",", 2) + if len(arr) < 2 { + return nil, errInvalidSyntax + } + kv := strings.Split(arr[0], "=") + if kv[0] != "type" { + return nil, errInvalidSyntax + } + switch kv[1] { + case "bind": + mountInfo.Type = string(cc.TypeBind) + case "tmpfs": + mountInfo.Type = string(cc.TypeTmpfs) + mountInfo.Source = string(cc.TypeTmpfs) + mountInfo.Options = append(mountInfo.Options, []string{"rprivate", "noexec", "nosuid", "nodev", "size=65536k"}...) + + default: + return nil, errors.Errorf("invalid filesystem type %q", kv[1]) + } + + tokens := strings.Split(arr[1], ",") + for i, val := range tokens { + if i == (tokenCount - 1) { + //Parse tokens before options. + break + } + kv := strings.Split(val, "=") + switch kv[0] { + case "ro", "nosuid", "nodev", "noexec": + mountInfo.Options = append(mountInfo.Options, kv[0]) + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z": + if mountInfo.Type != "bind" { + return nil, errors.Errorf("%s can only be used with bind mounts", kv[0]) + } + mountInfo.Options = append(mountInfo.Options, kv[0]) + case "tmpfs-mode": + if mountInfo.Type != "tmpfs" { + return nil, errors.Errorf("%s can only be used with tmpfs mounts", kv[0]) + } + mountInfo.Options = append(mountInfo.Options, fmt.Sprintf("mode=%s", kv[1])) + case "tmpfs-size": + if mountInfo.Type != "tmpfs" { + return nil, errors.Errorf("%s can only be used with tmpfs mounts", kv[0]) + } + shmSize, err := units.FromHumanSize(kv[1]) + if err != nil { + return nil, errors.Wrapf(err, "unable to translate tmpfs-size") + } + + mountInfo.Options = append(mountInfo.Options, fmt.Sprintf("size=%d", shmSize)) + + case "bind-propagation": + if mountInfo.Type != "bind" { + return nil, errors.Errorf("%s can only be used with bind mounts", kv[0]) + } + mountInfo.Options = append(mountInfo.Options, kv[1]) + case "src", "source": + if mountInfo.Type == "tmpfs" { + return nil, errors.Errorf("can not use src= on a tmpfs file system") + } + if err := validateVolumeHostDir(kv[1]); err != nil { + return nil, err + } + mountInfo.Source = kv[1] + case "target", "dst", "destination": + if err := validateVolumeCtrDir(kv[1]); err != nil { + return nil, err + } + mountInfo.Destination = kv[1] + default: + return nil, errors.Errorf("incorrect mount option : %s", kv[0]) + } + } + mountList = append(mountList, mountInfo) + } + return mountList, nil +} + func parseVolumes(volumes []string) error { for _, volume := range volumes { arr := strings.SplitN(volume, ":", 3) diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index b7281ed8c..a0d497e8e 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -7,6 +7,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" "github.com/urfave/cli" @@ -21,6 +22,21 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) { return GetRuntimeWithStorageOpts(c, &storageOpts) } +// GetContainerRuntime generates a new libpod runtime configured by command line options for containers +func GetContainerRuntime(c *cli.Context) (*libpod.Runtime, error) { + mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap")) + if err != nil { + return nil, err + } + storageOpts, err := GetDefaultStoreOptions() + if err != nil { + return nil, err + } + storageOpts.UIDMap = mappings.UIDMap + storageOpts.GIDMap = mappings.GIDMap + return GetRuntimeWithStorageOpts(c, &storageOpts) +} + func GetRootlessStorageOpts() (storage.StoreOptions, error) { var opts storage.StoreOptions @@ -120,7 +136,9 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions if c.GlobalIsSet("default-mounts-file") { options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file"))) } - options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path"), c.GlobalIsSet("hooks-dir-path"))) + if c.GlobalIsSet("hooks-dir-path") { + options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path"))) + } // TODO flag to set CNI plugins dir? diff --git a/cmd/podman/main.go b/cmd/podman/main.go index a532bc26e..840650a3f 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -147,6 +147,8 @@ func main() { if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { return errors.Wrapf(err, "error setting new rlimits") } + } else { + logrus.Info("running as rootless") } if logLevel == "debug" { diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 3445daef5..f9a96e4a6 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "fmt" "io/ioutil" "os" @@ -11,11 +10,7 @@ import ( "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/inspect" "github.com/containers/libpod/pkg/rootless" - cc "github.com/containers/libpod/pkg/spec" - "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -42,108 +37,24 @@ var runCommand = cli.Command{ } func runCmd(c *cli.Context) error { - var imageName string - - // Docker-compatibility: the "-h" flag for run/create is reserved for - // the hostname (see https://github.com/containers/libpod/issues/1367). - if c.Bool("help") { - cli.ShowCommandHelpAndExit(c, "run", 0) - } - - if err := validateFlags(c, createFlags); err != nil { - return err - } - - if c.String("cidfile") != "" { - if _, err := os.Stat(c.String("cidfile")); err == nil { - return errors.Errorf("container id file exists. ensure another container is not using it or delete %s", c.String("cidfile")) - } - if err := libpod.WriteFile("", c.String("cidfile")); err != nil { - return errors.Wrapf(err, "unable to write cidfile %s", c.String("cidfile")) - } - } - - storageOpts, err := libpodruntime.GetDefaultStoreOptions() - if err != nil { - return err - } - mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap")) - if err != nil { + if err := createInit(c); err != nil { return err } - storageOpts.UIDMap = mappings.UIDMap - storageOpts.GIDMap = mappings.GIDMap - if os.Geteuid() != 0 { rootless.SetSkipStorageSetup(true) } - runtime, err := libpodruntime.GetRuntimeWithStorageOpts(c, &storageOpts) + runtime, err := libpodruntime.GetContainerRuntime(c) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.Shutdown(false) - if len(c.Args()) < 1 { - return errors.Errorf("image name or ID is required") - } - - rootfs := "" - if c.Bool("rootfs") { - rootfs = c.Args()[0] - } - - ctx := getContext() - rtc := runtime.GetConfig() - - var newImage *image.Image = nil - var data *inspect.ImageData = nil - if rootfs == "" && !rootless.SkipStorageSetup() { - newImage, err = runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false, false) - if err != nil { - return errors.Wrapf(err, "unable to find image") - } - - data, err = newImage.Inspect(ctx) - if err != nil { - return err - } - if len(newImage.Names()) < 1 { - imageName = newImage.ID() - } else { - imageName = newImage.Names()[0] - } - } - createConfig, err := parseCreateOpts(ctx, c, runtime, imageName, data) + ctr, createConfig, err := createContainer(c, runtime) if err != nil { return err } - runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig) - if err != nil { - return err - } - - options, err := createConfig.GetContainerCreateOptions(runtime) - if err != nil { - return err - } - - became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, runtime) - if err != nil { - return err - } - if became { - os.Exit(ret) - } - - ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...) - if err != nil { - return err - } - - logrus.Debugf("New container created %q", ctr.ID()) - if logrus.GetLevel() == logrus.DebugLevel { cgroupPath, err := ctr.CGroupPath() if err == nil { @@ -151,20 +62,7 @@ func runCmd(c *cli.Context) error { } } - createConfigJSON, err := json.Marshal(createConfig) - if err != nil { - return err - } - if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil { - return err - } - - if c.String("cidfile") != "" { - if err := libpod.WriteFile(ctr.ID(), c.String("cidfile")); err != nil { - logrus.Error(err) - } - } - + ctx := getContext() // Handle detached start if createConfig.Detach { if err := ctr.Start(ctx); err != nil { diff --git a/cmd/podman/wait.go b/cmd/podman/wait.go index e919ab3ca..07db20eee 100644 --- a/cmd/podman/wait.go +++ b/cmd/podman/wait.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "time" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/pkg/errors" @@ -15,7 +16,14 @@ var ( Block until one or more containers stop and then print their exit codes ` - waitFlags = []cli.Flag{LatestFlag} + waitFlags = []cli.Flag{ + cli.UintFlag{ + Name: "interval, i", + Usage: "Milliseconds to wait before polling for completion", + Value: 250, + }, + LatestFlag, + } waitCommand = cli.Command{ Name: "wait", Usage: "Block on one or more containers", @@ -57,7 +65,10 @@ func waitCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "unable to find container %s", container) } - returnCode, err := ctr.Wait() + if c.Uint("interval") == 0 { + return errors.Errorf("interval must be greater then 0") + } + returnCode, err := ctr.WaitWithInterval(time.Duration(c.Uint("interval")) * time.Millisecond) if err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) |