2018-02-05 16:05:59 -05:00
|
|
|
package cli // import "github.com/docker/docker/integration-cli/cli"
|
2017-03-23 13:35:22 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-03-27 11:12:48 -04:00
|
|
|
"io"
|
|
|
|
"strings"
|
2019-09-23 07:54:51 -04:00
|
|
|
"testing"
|
2017-03-23 13:35:22 -04:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/docker/integration-cli/daemon"
|
|
|
|
"github.com/docker/docker/integration-cli/environment"
|
2017-03-27 11:12:48 -04:00
|
|
|
"github.com/pkg/errors"
|
2020-02-07 08:39:24 -05:00
|
|
|
"gotest.tools/v3/icmd"
|
2017-03-23 13:35:22 -04:00
|
|
|
)
|
|
|
|
|
2017-08-25 18:48:36 -04:00
|
|
|
var testEnv *environment.Execution
|
2017-03-23 13:35:22 -04:00
|
|
|
|
2017-08-25 18:48:36 -04:00
|
|
|
// SetTestEnvironment sets a static test environment
|
|
|
|
// TODO: decouple this package from environment
|
|
|
|
func SetTestEnvironment(env *environment.Execution) {
|
|
|
|
testEnv = env
|
2017-03-23 13:35:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// CmdOperator defines functions that can modify a command
|
|
|
|
type CmdOperator func(*icmd.Cmd) func()
|
|
|
|
|
|
|
|
// DockerCmd executes the specified docker command and expect a success
|
2019-09-23 07:54:51 -04:00
|
|
|
func DockerCmd(t testing.TB, args ...string) *icmd.Result {
|
2017-03-27 11:12:48 -04:00
|
|
|
return Docker(Args(args...)).Assert(t, icmd.Success)
|
2017-03-23 13:35:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// BuildCmd executes the specified docker build command and expect a success
|
2019-09-23 07:54:51 -04:00
|
|
|
func BuildCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
|
2017-03-23 13:35:22 -04:00
|
|
|
return Docker(Build(name), cmdOperators...).Assert(t, icmd.Success)
|
|
|
|
}
|
|
|
|
|
|
|
|
// InspectCmd executes the specified docker inspect command and expect a success
|
2019-09-23 07:54:51 -04:00
|
|
|
func InspectCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
|
2017-03-23 13:35:22 -04:00
|
|
|
return Docker(Inspect(name), cmdOperators...).Assert(t, icmd.Success)
|
|
|
|
}
|
|
|
|
|
2017-04-11 15:18:30 -04:00
|
|
|
// WaitRun will wait for the specified container to be running, maximum 5 seconds.
|
2019-09-23 07:54:51 -04:00
|
|
|
func WaitRun(t testing.TB, name string, cmdOperators ...CmdOperator) {
|
2017-04-11 15:18:30 -04:00
|
|
|
WaitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WaitExited will wait for the specified container to state exit, subject
|
|
|
|
// to a maximum time limit in seconds supplied by the caller
|
2019-09-23 07:54:51 -04:00
|
|
|
func WaitExited(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
|
2017-04-11 15:18:30 -04:00
|
|
|
WaitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WaitRestart will wait for the specified container to restart once
|
2019-09-23 07:54:51 -04:00
|
|
|
func WaitRestart(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
|
2017-04-11 15:18:30 -04:00
|
|
|
WaitForInspectResult(t, name, "{{.RestartCount}}", "1", timeout, cmdOperators...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WaitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time.
|
2019-09-23 07:54:51 -04:00
|
|
|
func WaitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) {
|
2017-04-11 15:18:30 -04:00
|
|
|
after := time.After(timeout)
|
|
|
|
|
|
|
|
args := []string{"inspect", "-f", expr, name}
|
|
|
|
for {
|
|
|
|
result := Docker(Args(args...), cmdOperators...)
|
|
|
|
if result.Error != nil {
|
|
|
|
if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
|
|
|
|
t.Fatalf("error executing docker inspect: %v\n%s",
|
|
|
|
result.Stderr(), result.Stdout())
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-after:
|
|
|
|
t.Fatal(result.Error)
|
|
|
|
default:
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out := strings.TrimSpace(result.Stdout())
|
|
|
|
if out == expected {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-after:
|
|
|
|
t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-23 13:35:22 -04:00
|
|
|
// Docker executes the specified docker command
|
|
|
|
func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result {
|
|
|
|
for _, op := range cmdOperators {
|
|
|
|
deferFn := op(&cmd)
|
|
|
|
if deferFn != nil {
|
|
|
|
defer deferFn()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
appendDocker(&cmd)
|
2017-03-27 11:12:48 -04:00
|
|
|
if err := validateArgs(cmd.Command...); err != nil {
|
|
|
|
return &icmd.Result{
|
|
|
|
Error: err,
|
|
|
|
}
|
|
|
|
}
|
2017-03-23 13:35:22 -04:00
|
|
|
return icmd.RunCmd(cmd)
|
|
|
|
}
|
|
|
|
|
2017-03-27 11:12:48 -04:00
|
|
|
// validateArgs is a checker to ensure tests are not running commands which are
|
|
|
|
// not supported on platforms. Specifically on Windows this is 'busybox top'.
|
|
|
|
func validateArgs(args ...string) error {
|
2017-09-20 08:47:49 -04:00
|
|
|
if testEnv.OSType != "windows" {
|
2017-03-27 11:12:48 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
foundBusybox := -1
|
|
|
|
for key, value := range args {
|
|
|
|
if strings.ToLower(value) == "busybox" {
|
|
|
|
foundBusybox = key
|
|
|
|
}
|
|
|
|
if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") {
|
|
|
|
return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-23 13:35:22 -04:00
|
|
|
// Build executes the specified docker build command
|
|
|
|
func Build(name string) icmd.Cmd {
|
|
|
|
return icmd.Command("build", "-t", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inspect executes the specified docker inspect command
|
|
|
|
func Inspect(name string) icmd.Cmd {
|
|
|
|
return icmd.Command("inspect", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format sets the specified format with --format flag
|
|
|
|
func Format(format string) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Command = append(
|
|
|
|
[]string{cmd.Command[0]},
|
|
|
|
append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)...,
|
|
|
|
)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func appendDocker(cmd *icmd.Cmd) {
|
|
|
|
cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...)
|
|
|
|
}
|
|
|
|
|
2017-03-27 11:12:48 -04:00
|
|
|
// Args build an icmd.Cmd struct from the specified arguments
|
|
|
|
func Args(args ...string) icmd.Cmd {
|
|
|
|
switch len(args) {
|
|
|
|
case 0:
|
|
|
|
return icmd.Cmd{}
|
|
|
|
case 1:
|
|
|
|
return icmd.Command(args[0])
|
|
|
|
default:
|
|
|
|
return icmd.Command(args[0], args[1:]...)
|
|
|
|
}
|
2017-03-23 13:35:22 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Daemon points to the specified daemon
|
|
|
|
func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithTimeout sets the timeout for the command to run
|
|
|
|
func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Timeout = timeout
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithEnvironmentVariables sets the specified environment variables for the command to run
|
|
|
|
func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Env = envs
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithFlags sets the specified flags for the command to run
|
|
|
|
func WithFlags(flags ...string) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Command = append(cmd.Command, flags...)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2017-03-27 11:12:48 -04:00
|
|
|
|
|
|
|
// InDir sets the folder in which the command should be executed
|
|
|
|
func InDir(path string) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Dir = path
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithStdout sets the standard output writer of the command
|
|
|
|
func WithStdout(writer io.Writer) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Stdout = writer
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2017-08-22 17:07:52 -04:00
|
|
|
|
|
|
|
// WithStdin sets the standard input reader for the command
|
|
|
|
func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() {
|
|
|
|
return func(cmd *icmd.Cmd) func() {
|
|
|
|
cmd.Stdin = stdin
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|