diff options
author | baude <bbaude@redhat.com> | 2018-09-21 09:43:54 -0500 |
---|---|---|
committer | baude <bbaude@redhat.com> | 2018-09-28 14:14:13 -0500 |
commit | 4f825f2e079c1cf3ec6c9fd2c5378ce2db18d4f0 (patch) | |
tree | 6c237b29beb1bebcb94fa02250e1931f4b129240 /cmd/podman | |
parent | 7b152a24be224ee454b3f698cc1c1ed71330a476 (diff) | |
download | podman-4f825f2e079c1cf3ec6c9fd2c5378ce2db18d4f0.tar.gz podman-4f825f2e079c1cf3ec6c9fd2c5378ce2db18d4f0.tar.bz2 podman-4f825f2e079c1cf3ec6c9fd2c5378ce2db18d4f0.zip |
Add container runlabel command
Execute the command as described by a container image. The value of the label is processed
into a command by:
1. Ensuring the first argument of the command is podman.
2. Substituting any variables with those defined by the environment or otherwise.
If no label exists in the container image, nothing is done.
podman container runlabel LABEL IMAGE extra_args
Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'cmd/podman')
-rw-r--r-- | cmd/podman/container.go | 1 | ||||
-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 |
4 files changed, 335 insertions, 0 deletions
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/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)) +} |