summaryrefslogtreecommitdiff
path: root/cmd/podman
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2018-10-02 17:16:43 -0700
committerGitHub <noreply@github.com>2018-10-02 17:16:43 -0700
commitd5687946f6a0aa14a5326f26ca0ceca68588056f (patch)
tree888c74c05f061cda2f53829969634e31a0d41643 /cmd/podman
parentb63b1f9cb638db4bc3fdf69a50857b6d04efb6d7 (diff)
parent4f825f2e079c1cf3ec6c9fd2c5378ce2db18d4f0 (diff)
downloadpodman-d5687946f6a0aa14a5326f26ca0ceca68588056f.tar.gz
podman-d5687946f6a0aa14a5326f26ca0ceca68588056f.tar.bz2
podman-d5687946f6a0aa14a5326f26ca0ceca68588056f.zip
Merge pull request #1528 from baude/runlabel
Add container runlabel command
Diffstat (limited to 'cmd/podman')
-rw-r--r--cmd/podman/container.go1
-rw-r--r--cmd/podman/runlabel.go188
-rw-r--r--cmd/podman/shared/funcs.go57
-rw-r--r--cmd/podman/shared/funcs_test.go89
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))
+}