diff options
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/cleanup.go | 4 | ||||
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-rw-r--r-- | cmd/podman/create.go | 4 | ||||
-rw-r--r-- | cmd/podman/kill.go | 64 | ||||
-rw-r--r-- | cmd/podman/main.go | 5 | ||||
-rw-r--r-- | cmd/podman/pod_stop.go | 4 | ||||
-rw-r--r-- | cmd/podman/ps.go | 2 | ||||
-rw-r--r-- | cmd/podman/run.go | 2 | ||||
-rw-r--r-- | cmd/podman/runlabel.go | 188 | ||||
-rw-r--r-- | cmd/podman/shared/funcs.go | 57 | ||||
-rw-r--r-- | cmd/podman/shared/funcs_test.go | 89 | ||||
-rw-r--r-- | cmd/podman/start.go | 7 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 4 |
13 files changed, 406 insertions, 25 deletions
diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index 6ebb682ed..316704f91 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -46,6 +46,8 @@ func cleanupCmd(c *cli.Context) error { args := c.Args() + ctx := getContext() + var lastError error var cleanupContainers []*libpod.Container if c.Bool("all") { @@ -80,7 +82,7 @@ func cleanupCmd(c *cli.Context) error { } } for _, ctr := range cleanupContainers { - if err = ctr.Cleanup(); err != nil { + if err = ctr.Cleanup(ctx); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } diff --git a/cmd/podman/container.go b/cmd/podman/container.go index b73fb7a94..82c1c824d 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -25,6 +25,7 @@ var ( restartCommand, rmCommand, runCommand, + runlabelCommand, startCommand, statsCommand, stopCommand, diff --git a/cmd/podman/create.go b/cmd/podman/create.go index fc0c71536..574137271 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -784,7 +784,9 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim VolumesFrom: c.StringSlice("volumes-from"), } - if !config.Privileged { + if config.Privileged { + config.LabelOpts = label.DisableSecOpt() + } else { if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil { return nil, err } diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index f80d77b8f..db3300984 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/docker/docker/pkg/signal" "github.com/pkg/errors" @@ -14,6 +15,10 @@ import ( var ( killFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "Signal all running containers", + }, cli.StringFlag{ Name: "signal, s", Usage: "Signal to send to the container", @@ -28,7 +33,7 @@ var ( Description: killDescription, Flags: killFlags, Action: killCmd, - ArgsUsage: "[CONTAINER_NAME_OR_ID]", + ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", UseShortOptionHandling: true, OnUsageError: usageErrorHandler, } @@ -37,11 +42,17 @@ var ( // killCmd kills one or more containers with a signal func killCmd(c *cli.Context) error { args := c.Args() - if len(args) == 0 && !c.Bool("latest") { - return errors.Errorf("specify one or more containers to kill") + if (!c.Bool("all") && !c.Bool("latest")) && len(args) == 0 { + return errors.Errorf("you must specify one or more containers to kill") + } + if (c.Bool("all") || c.Bool("latest")) && len(args) > 0 { + return errors.Errorf("you cannot specify any containers to kill with --latest or --all") + } + if c.Bool("all") && c.Bool("latest") { + return errors.Errorf("--all and --latest cannot be used together") } - if len(args) > 0 && c.Bool("latest") { - return errors.Errorf("you cannot specific any containers to kill with --latest") + if len(args) < 1 && !c.Bool("all") && !c.Bool("latest") { + return errors.Errorf("you must provide at least one container name or id") } if err := validateFlags(c, killFlags); err != nil { return err @@ -65,30 +76,45 @@ func killCmd(c *cli.Context) error { killSignal = uint(sysSignal) } - if c.Bool("latest") { - latestCtr, err := runtime.GetLatestContainer() + var filterFuncs []libpod.ContainerFilter + var containers []*libpod.Container + var lastError error + if c.Bool("all") { + // only get running containers + filterFuncs = append(filterFuncs, func(c *libpod.Container) bool { + state, _ := c.State() + return state == libpod.ContainerStateRunning + }) + containers, err = runtime.GetContainers(filterFuncs...) if err != nil { - return errors.Wrapf(err, "unable to get latest container") + return errors.Wrapf(err, "unable to get running containers") } - args = append(args, latestCtr.ID()) - } - - var lastError error - for _, container := range args { - ctr, err := runtime.LookupContainer(container) + } else if c.Bool("latest") { + lastCtr, err := runtime.GetLatestContainer() if err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + return errors.Wrapf(err, "unable to get last created container") + } + containers = append(containers, lastCtr) + } else { + for _, i := range args { + container, err := runtime.LookupContainer(i) + if err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "unable to find container %s", i) + continue } - lastError = errors.Wrapf(err, "unable to find container %v", container) - continue + containers = append(containers, container) } + } + for _, ctr := range containers { if err := ctr.Kill(killSignal); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } - lastError = errors.Wrapf(err, "unable to find container %v", container) + lastError = errors.Wrapf(err, "unable to find container %v", ctr.ID()) } else { fmt.Println(ctr.ID()) } diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 840650a3f..d4c8454a8 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -7,6 +7,7 @@ import ( "runtime/pprof" "syscall" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/hooks" _ "github.com/containers/libpod/pkg/hooks/0.1.0" "github.com/containers/libpod/pkg/rootless" @@ -109,6 +110,10 @@ func main() { } app.Before = func(c *cli.Context) error { + if err := libpod.SetXdgRuntimeDir(""); err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } args := c.Args() if args.Present() { if _, notRequireRootless := cmdsNotRequiringRootless[args.First()]; !notRequireRootless { diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index 03d04a3ec..6dc6a2b2d 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -50,9 +50,11 @@ func podStopCmd(c *cli.Context) error { // in which case the following loop will be skipped. pods, lastError := getPodsFromContext(c, runtime) + ctx := getContext() + for _, pod := range pods { // set cleanup to true to clean mounts and namespaces - ctr_errs, err := pod.Stop(true) + ctr_errs, err := pod.Stop(ctx, true) if ctr_errs != nil { for ctr, err := range ctr_errs { if lastError != nil { diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index d36c929e8..e53afe1bf 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -548,6 +548,8 @@ func getTemplateOutput(psParams []psJSONParams, opts shared.PsOptions) ([]psTemp labels := formatLabels(psParam.Labels) switch psParam.Status { + case libpod.ContainerStateExited.String(): + fallthrough case libpod.ContainerStateStopped.String(): exitedSince := units.HumanDuration(time.Since(psParam.ExitedAt)) status = fmt.Sprintf("Exited (%d) %s ago", psParam.ExitCode, exitedSince) diff --git a/cmd/podman/run.go b/cmd/podman/run.go index f9a96e4a6..fbad4237d 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -140,7 +140,7 @@ func runCmd(c *cli.Context) error { return runtime.RemoveContainer(ctx, ctr, true) } - if err := ctr.Cleanup(); err != nil { + if err := ctr.Cleanup(ctx); err != nil { // If the container has been removed already, no need to error on cleanup // Also, if it was restarted, don't error either if errors.Cause(err) == libpod.ErrNoSuchCtr || diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go new file mode 100644 index 000000000..c5dd98ee6 --- /dev/null +++ b/cmd/podman/runlabel.go @@ -0,0 +1,188 @@ +package main + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/utils" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var ( + runlabelFlags = []cli.Flag{ + cli.StringFlag{ + Name: "authfile", + Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json", + }, + cli.BoolFlag{ + Name: "display", + Usage: "preview the command that `podman install` would execute", + }, + cli.StringFlag{ + Name: "cert-dir", + Usage: "`pathname` of a directory containing TLS certificates and keys", + }, + cli.StringFlag{ + Name: "creds", + Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry", + }, + cli.StringFlag{ + Name: "name", + Usage: "Assign a name to the container", + }, + cli.StringFlag{ + Name: "opt1", + Usage: "Optional parameter to pass for install", + Hidden: true, + }, + cli.StringFlag{ + Name: "opt2", + Usage: "Optional parameter to pass for install", + Hidden: true, + }, + cli.StringFlag{ + Name: "opt3", + Usage: "Optional parameter to pass for install", + Hidden: true, + }, + cli.BoolFlag{ + Name: "quiet, q", + Usage: "Suppress output information when installing images", + }, + cli.BoolFlag{ + Name: "pull, p", + Usage: "pull the image if it does not exist locally prior to executing the label contents", + }, + cli.StringFlag{ + Name: "signature-policy", + Usage: "`pathname` of signature policy file (not usually used)", + }, + cli.BoolTFlag{ + Name: "tls-verify", + Usage: "require HTTPS and verify certificates when contacting registries (default: true)", + }, + } + + runlabelDescription = ` +Executes a command as described by a container image label. +` + runlabelCommand = cli.Command{ + Name: "runlabel", + Usage: "Execute the command described by an image label", + Description: runlabelDescription, + Flags: runlabelFlags, + Action: runlabelCmd, + ArgsUsage: "", + OnUsageError: usageErrorHandler, + } +) + +// installCmd gets the data from the command line and calls installImage +// to copy an image from a registry to a local machine +func runlabelCmd(c *cli.Context) error { + var ( + imageName string + stdErr, stdOut io.Writer + stdIn io.Reader + newImage *image.Image + ) + + opts := make(map[string]string) + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + args := c.Args() + if len(args) == 0 { + logrus.Errorf("an image name must be specified") + return nil + } + if len(args) < 2 { + logrus.Errorf("the runlabel command requires at least 2 arguments") + return nil + } + if err := validateFlags(c, runlabelFlags); err != nil { + return err + } + if c.Bool("display") && c.Bool("quiet") { + return errors.Errorf("the display and quiet flags cannot be used together.") + } + + pull := c.Bool("pull") + label := args[0] + + runlabelImage := args[1] + + if c.IsSet("opts1") { + opts["opts1"] = c.String("opts1") + } + if c.IsSet("opts2") { + opts["opts2"] = c.String("opts2") + } + if c.IsSet("opts3") { + opts["opts3"] = c.String("opts3") + } + + ctx := getContext() + rtc := runtime.GetConfig() + + stdErr = os.Stderr + stdOut = os.Stdout + stdIn = os.Stdin + + if c.Bool("quiet") { + stdErr = nil + stdOut = nil + stdIn = nil + } + + if pull { + newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, rtc.SignaturePolicyPath, "", stdOut, nil, image.SigningOptions{}, false, false) + } else { + newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage) + } + if err != nil { + return errors.Wrapf(err, "unable to find image") + } + + if len(newImage.Names()) < 1 { + imageName = newImage.ID() + } else { + imageName = newImage.Names()[0] + } + + runLabel, err := newImage.GetLabel(ctx, label) + if err != nil { + return err + } + + // If no label to execute, we return + if runLabel == "" { + return nil + } + + // The user provided extra arguments that need to be tacked onto the label's command + if len(args) > 2 { + runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " ")) + } + + cmd := shared.GenerateCommand(runLabel, imageName, c.String("name")) + env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts) + + if !c.Bool("quiet") { + fmt.Printf("Command: %s\n", strings.Join(cmd, " ")) + if c.Bool("display") { + return nil + } + } + return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...) +} diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go new file mode 100644 index 000000000..5c401634c --- /dev/null +++ b/cmd/podman/shared/funcs.go @@ -0,0 +1,57 @@ +package shared + +import ( + "fmt" + "os" + "strings" +) + +// GenerateCommand takes a label (string) and converts it to an executable command +func GenerateCommand(command, imageName, name string) []string { + var ( + newCommand []string + ) + if name == "" { + name = imageName + } + cmd := strings.Split(command, " ") + // Replace the first position of cmd with podman whether + // it is docker, /usr/bin/docker, or podman + newCommand = append(newCommand, "podman") + for _, arg := range cmd[1:] { + var newArg string + switch arg { + case "IMAGE": + newArg = imageName + case "IMAGE=IMAGE": + newArg = fmt.Sprintf("IMAGE=%s", imageName) + case "NAME": + newArg = name + case "NAME=NAME": + newArg = fmt.Sprintf("NAME=%s", name) + default: + newArg = arg + } + newCommand = append(newCommand, newArg) + } + return newCommand +} + +// GenerateRunEnvironment merges the current environment variables with optional +// environment variables provided by the user +func GenerateRunEnvironment(name, imageName string, opts map[string]string) []string { + newEnv := os.Environ() + newEnv = append(newEnv, fmt.Sprintf("NAME=%s", name)) + newEnv = append(newEnv, fmt.Sprintf("IMAGE=%s", imageName)) + + if opts["opt1"] != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", opts["opt1"])) + } + if opts["opt2"] != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", opts["opt2"])) + } + if opts["opt3"] != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", opts["opt3"])) + } + return newEnv +} diff --git a/cmd/podman/shared/funcs_test.go b/cmd/podman/shared/funcs_test.go new file mode 100644 index 000000000..3d0ac005f --- /dev/null +++ b/cmd/podman/shared/funcs_test.go @@ -0,0 +1,89 @@ +package shared + +import ( + "strings" + "testing" + + "github.com/containers/libpod/pkg/util" + "github.com/stretchr/testify/assert" +) + +var ( + name = "foo" + imageName = "bar" +) + +func TestGenerateCommand(t *testing.T) { + inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install" + correctCommand := "podman run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install" + newCommand := GenerateCommand(inputCommand, "foo", "bar") + assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) +} + +func TestGenerateCommandPath(t *testing.T) { + inputCommand := "/usr/bin/docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install" + correctCommand := "podman run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install" + newCommand := GenerateCommand(inputCommand, "foo", "bar") + assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) +} + +func TestGenerateCommandNoSetName(t *testing.T) { + inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install" + correctCommand := "podman run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install" + newCommand := GenerateCommand(inputCommand, "foo", "") + assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) +} + +func TestGenerateCommandNoName(t *testing.T) { + inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install" + correctCommand := "podman run -it -e IMAGE=foo foo echo install" + newCommand := GenerateCommand(inputCommand, "foo", "") + assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) +} + +func TestGenerateCommandAlreadyPodman(t *testing.T) { + inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install" + correctCommand := "podman run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install" + newCommand := GenerateCommand(inputCommand, "foo", "bar") + assert.Equal(t, correctCommand, strings.Join(newCommand, " ")) +} + +func TestGenerateRunEnvironment(t *testing.T) { + opts := make(map[string]string) + opts["opt1"] = "one" + opts["opt2"] = "two" + opts["opt3"] = "three" + envs := GenerateRunEnvironment(name, imageName, opts) + assert.True(t, util.StringInSlice("OPT1=one", envs)) + assert.True(t, util.StringInSlice("OPT2=two", envs)) + assert.True(t, util.StringInSlice("OPT3=three", envs)) +} + +func TestGenerateRunEnvironmentNoOpts(t *testing.T) { + opts := make(map[string]string) + envs := GenerateRunEnvironment(name, imageName, opts) + assert.False(t, util.StringInSlice("OPT1=", envs)) + assert.False(t, util.StringInSlice("OPT2=", envs)) + assert.False(t, util.StringInSlice("OPT3=", envs)) +} + +func TestGenerateRunEnvironmentSingleOpt(t *testing.T) { + opts := make(map[string]string) + opts["opt1"] = "one" + envs := GenerateRunEnvironment(name, imageName, opts) + assert.True(t, util.StringInSlice("OPT1=one", envs)) + assert.False(t, util.StringInSlice("OPT2=", envs)) + assert.False(t, util.StringInSlice("OPT3=", envs)) +} + +func TestGenerateRunEnvironmentName(t *testing.T) { + opts := make(map[string]string) + envs := GenerateRunEnvironment(name, imageName, opts) + assert.True(t, util.StringInSlice("NAME=foo", envs)) +} + +func TestGenerateRunEnvironmentImage(t *testing.T) { + opts := make(map[string]string) + envs := GenerateRunEnvironment(name, imageName, opts) + assert.True(t, util.StringInSlice("IMAGE=bar", envs)) +} diff --git a/cmd/podman/start.go b/cmd/podman/start.go index cb65ec6d4..a34f6df5d 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -81,6 +81,9 @@ func startCmd(c *cli.Context) error { } args = append(args, lastCtr.ID()) } + + ctx := getContext() + var lastError error for _, container := range args { ctr, err := runtime.LookupContainer(container) @@ -121,14 +124,14 @@ func startCmd(c *cli.Context) error { exitCode = int(ecode) } - return ctr.Cleanup() + return ctr.Cleanup(ctx) } if ctrRunning { fmt.Println(ctr.ID()) continue } // Handle non-attach start - if err := ctr.Start(getContext()); err != nil { + if err := ctr.Start(ctx); err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 810f757ca..4a4a1854c 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -820,6 +820,10 @@ error PodNotFound (name: string) # a container ID of the container that failed. error PodContainerError (podname: string, errors: []PodContainerErrorData) +# NoContainersInPod means a pod has no containers on which to perform the operation. It contains +# the pod ID. +error NoContainersInPod (name: string) + # ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message # is includes as part of the error's text. error ErrorOccurred (reason: string) |