Merge pull request #1637 from vrothberg/runlabel-execute-any-command

runlabel: run any command
This commit is contained in:
OpenShift Merge Robot
2018-10-26 04:33:12 -07:00
committed by GitHub
4 changed files with 190 additions and 11 deletions

View File

@ -196,7 +196,10 @@ func runlabelCmd(c *cli.Context) error {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
}
cmd := shared.GenerateCommand(runLabel, imageName, c.String("name"))
cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
if err != nil {
return errors.Wrapf(err, "unable to generate command")
}
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")

View File

@ -3,11 +3,39 @@ package shared
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func substituteCommand(cmd string) (string, error) {
// If cmd is an absolute or relative path, check if the file exists.
// Throw an error if it doesn't exist.
if strings.Contains(cmd, "/") || strings.HasPrefix(cmd, ".") {
res, err := filepath.Abs(cmd)
if err != nil {
return "", err
}
if _, err := os.Stat(res); !os.IsNotExist(err) {
return res, nil
} else if err != nil {
return "", err
}
}
// Replace cmd with "/proc/self/exe" if "podman" or "docker" is being
// used. Otherwise, leave the command unchanged.
switch cmd {
case "podman":
fallthrough
case "docker":
return "/proc/self/exe", nil
default:
return cmd, nil
}
}
// GenerateCommand takes a label (string) and converts it to an executable command
func GenerateCommand(command, imageName, name string) []string {
func GenerateCommand(command, imageName, name string) ([]string, error) {
var (
newCommand []string
)
@ -15,8 +43,13 @@ func GenerateCommand(command, imageName, name string) []string {
name = imageName
}
cmd := strings.Split(command, " ")
// Replace the first element of cmd with "/proc/self/exe"
newCommand = append(newCommand, "/proc/self/exe")
prog, err := substituteCommand(cmd[0])
if err != nil {
return nil, err
}
newCommand = append(newCommand, prog)
for _, arg := range cmd[1:] {
var newArg string
switch arg {
@ -37,7 +70,7 @@ func GenerateCommand(command, imageName, name string) []string {
}
newCommand = append(newCommand, newArg)
}
return newCommand
return newCommand, nil
}
// GenerateRunEnvironment merges the current environment variables with optional

View File

@ -1,6 +1,10 @@
package shared
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
@ -16,35 +20,106 @@ var (
func TestGenerateCommand(t *testing.T) {
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
newCommand := GenerateCommand(inputCommand, "foo", "bar")
newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
func TestGenerateCommandCheckSubstitution(t *testing.T) {
type subsTest struct {
input string
expected string
shouldFail bool
}
absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
assert.Nil(t, err, "error creating tempfile")
defer os.Remove(absTmpFile.Name())
relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
assert.Nil(t, err, "error creating tempfile")
defer os.Remove(relTmpFile.Name())
relTmpCmd, err := filepath.Abs(relTmpFile.Name())
assert.Nil(t, err, "error getting absolute path for relative tmpfile")
// this has a (low) potential of race conditions but no other way
removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
assert.Nil(t, err, "error creating tempfile")
os.Remove(removedTmpFile.Name())
absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
tests := []subsTest{
{
input: "docker run -it alpine:latest",
expected: "/proc/self/exe run -it alpine:latest",
shouldFail: false,
},
{
input: "podman run -it alpine:latest",
expected: "/proc/self/exe run -it alpine:latest",
shouldFail: false,
},
{
input: absTmpCmd,
expected: absTmpCmd,
shouldFail: false,
},
{
input: "./" + relTmpFile.Name(),
expected: relTmpCmd,
shouldFail: false,
},
{
input: "ls -la",
expected: "ls -la",
shouldFail: false,
},
{
input: removedTmpFile.Name(),
expected: "",
shouldFail: true,
},
}
for _, test := range tests {
newCommand, err := GenerateCommand(test.input, "foo", "bar")
if test.shouldFail {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
}
assert.Equal(t, test.expected, 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"
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
newCommand := GenerateCommand(inputCommand, "foo", "bar")
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 := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
newCommand := GenerateCommand(inputCommand, "foo", "")
newCommand, err := GenerateCommand(inputCommand, "foo", "")
assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
func TestGenerateCommandNoName(t *testing.T) {
inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
newCommand := GenerateCommand(inputCommand, "foo", "")
newCommand, err := GenerateCommand(inputCommand, "foo", "")
assert.Nil(t, err)
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 := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
newCommand := GenerateCommand(inputCommand, "foo", "bar")
newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}

68
test/e2e/runlabel_test.go Normal file
View File

@ -0,0 +1,68 @@
package integration
import (
"fmt"
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var PodmanDockerfile = `
FROM alpine:latest
LABEL RUN podman --version`
var LsDockerfile = `
FROM alpine:latest
LABEL RUN ls -la`
var _ = Describe("podman container runlabel", func() {
var (
tempdir string
err error
podmanTest PodmanTest
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest = PodmanCreate(tempdir)
podmanTest.RestoreAllArtifacts()
})
AfterEach(func() {
podmanTest.Cleanup()
f := CurrentGinkgoTestDescription()
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
GinkgoWriter.Write([]byte(timedResult))
})
It("podman container runlabel (podman --version)", func() {
image := "podman-runlabel-test:podman"
podmanTest.BuildImage(PodmanDockerfile, image, "false")
result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
result = podmanTest.Podman([]string{"rmi", image})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
})
It("podman container runlabel (ls -la)", func() {
image := "podman-runlabel-test:ls"
podmanTest.BuildImage(LsDockerfile, image, "false")
result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
result = podmanTest.Podman([]string{"rmi", image})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
})
})