github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/integration-cli/cli/cli.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/docker/docker/integration-cli/daemon"
    10  	"github.com/docker/docker/integration-cli/environment"
    11  	"github.com/gotestyourself/gotestyourself/icmd"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  var testEnv *environment.Execution
    16  
    17  // SetTestEnvironment sets a static test environment
    18  // TODO: decouple this package from environment
    19  func SetTestEnvironment(env *environment.Execution) {
    20  	testEnv = env
    21  }
    22  
    23  // CmdOperator defines functions that can modify a command
    24  type CmdOperator func(*icmd.Cmd) func()
    25  
    26  type testingT interface {
    27  	Fatal(args ...interface{})
    28  	Fatalf(string, ...interface{})
    29  }
    30  
    31  // DockerCmd executes the specified docker command and expect a success
    32  func DockerCmd(t testingT, args ...string) *icmd.Result {
    33  	return Docker(Args(args...)).Assert(t, icmd.Success)
    34  }
    35  
    36  // BuildCmd executes the specified docker build command and expect a success
    37  func BuildCmd(t testingT, name string, cmdOperators ...CmdOperator) *icmd.Result {
    38  	return Docker(Build(name), cmdOperators...).Assert(t, icmd.Success)
    39  }
    40  
    41  // InspectCmd executes the specified docker inspect command and expect a success
    42  func InspectCmd(t testingT, name string, cmdOperators ...CmdOperator) *icmd.Result {
    43  	return Docker(Inspect(name), cmdOperators...).Assert(t, icmd.Success)
    44  }
    45  
    46  // WaitRun will wait for the specified container to be running, maximum 5 seconds.
    47  func WaitRun(t testingT, name string, cmdOperators ...CmdOperator) {
    48  	WaitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...)
    49  }
    50  
    51  // WaitExited will wait for the specified container to state exit, subject
    52  // to a maximum time limit in seconds supplied by the caller
    53  func WaitExited(t testingT, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
    54  	WaitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...)
    55  }
    56  
    57  // WaitRestart will wait for the specified container to restart once
    58  func WaitRestart(t testingT, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
    59  	WaitForInspectResult(t, name, "{{.RestartCount}}", "1", timeout, cmdOperators...)
    60  }
    61  
    62  // WaitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time.
    63  func WaitForInspectResult(t testingT, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) {
    64  	after := time.After(timeout)
    65  
    66  	args := []string{"inspect", "-f", expr, name}
    67  	for {
    68  		result := Docker(Args(args...), cmdOperators...)
    69  		if result.Error != nil {
    70  			if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
    71  				t.Fatalf("error executing docker inspect: %v\n%s",
    72  					result.Stderr(), result.Stdout())
    73  			}
    74  			select {
    75  			case <-after:
    76  				t.Fatal(result.Error)
    77  			default:
    78  				time.Sleep(10 * time.Millisecond)
    79  				continue
    80  			}
    81  		}
    82  
    83  		out := strings.TrimSpace(result.Stdout())
    84  		if out == expected {
    85  			break
    86  		}
    87  
    88  		select {
    89  		case <-after:
    90  			t.Fatalf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
    91  		default:
    92  		}
    93  
    94  		time.Sleep(100 * time.Millisecond)
    95  	}
    96  }
    97  
    98  // Docker executes the specified docker command
    99  func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result {
   100  	for _, op := range cmdOperators {
   101  		deferFn := op(&cmd)
   102  		if deferFn != nil {
   103  			defer deferFn()
   104  		}
   105  	}
   106  	appendDocker(&cmd)
   107  	if err := validateArgs(cmd.Command...); err != nil {
   108  		return &icmd.Result{
   109  			Error: err,
   110  		}
   111  	}
   112  	return icmd.RunCmd(cmd)
   113  }
   114  
   115  // validateArgs is a checker to ensure tests are not running commands which are
   116  // not supported on platforms. Specifically on Windows this is 'busybox top'.
   117  func validateArgs(args ...string) error {
   118  	if testEnv.OSType != "windows" {
   119  		return nil
   120  	}
   121  	foundBusybox := -1
   122  	for key, value := range args {
   123  		if strings.ToLower(value) == "busybox" {
   124  			foundBusybox = key
   125  		}
   126  		if (foundBusybox != -1) && (key == foundBusybox+1) && (strings.ToLower(value) == "top") {
   127  			return errors.New("cannot use 'busybox top' in tests on Windows. Use runSleepingContainer()")
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // Build executes the specified docker build command
   134  func Build(name string) icmd.Cmd {
   135  	return icmd.Command("build", "-t", name)
   136  }
   137  
   138  // Inspect executes the specified docker inspect command
   139  func Inspect(name string) icmd.Cmd {
   140  	return icmd.Command("inspect", name)
   141  }
   142  
   143  // Format sets the specified format with --format flag
   144  func Format(format string) func(*icmd.Cmd) func() {
   145  	return func(cmd *icmd.Cmd) func() {
   146  		cmd.Command = append(
   147  			[]string{cmd.Command[0]},
   148  			append([]string{"--format", fmt.Sprintf("{{%s}}", format)}, cmd.Command[1:]...)...,
   149  		)
   150  		return nil
   151  	}
   152  }
   153  
   154  func appendDocker(cmd *icmd.Cmd) {
   155  	cmd.Command = append([]string{testEnv.DockerBinary()}, cmd.Command...)
   156  }
   157  
   158  // Args build an icmd.Cmd struct from the specified arguments
   159  func Args(args ...string) icmd.Cmd {
   160  	switch len(args) {
   161  	case 0:
   162  		return icmd.Cmd{}
   163  	case 1:
   164  		return icmd.Command(args[0])
   165  	default:
   166  		return icmd.Command(args[0], args[1:]...)
   167  	}
   168  }
   169  
   170  // Daemon points to the specified daemon
   171  func Daemon(d *daemon.Daemon) func(*icmd.Cmd) func() {
   172  	return func(cmd *icmd.Cmd) func() {
   173  		cmd.Command = append([]string{"--host", d.Sock()}, cmd.Command...)
   174  		return nil
   175  	}
   176  }
   177  
   178  // WithTimeout sets the timeout for the command to run
   179  func WithTimeout(timeout time.Duration) func(cmd *icmd.Cmd) func() {
   180  	return func(cmd *icmd.Cmd) func() {
   181  		cmd.Timeout = timeout
   182  		return nil
   183  	}
   184  }
   185  
   186  // WithEnvironmentVariables sets the specified environment variables for the command to run
   187  func WithEnvironmentVariables(envs ...string) func(cmd *icmd.Cmd) func() {
   188  	return func(cmd *icmd.Cmd) func() {
   189  		cmd.Env = envs
   190  		return nil
   191  	}
   192  }
   193  
   194  // WithFlags sets the specified flags for the command to run
   195  func WithFlags(flags ...string) func(*icmd.Cmd) func() {
   196  	return func(cmd *icmd.Cmd) func() {
   197  		cmd.Command = append(cmd.Command, flags...)
   198  		return nil
   199  	}
   200  }
   201  
   202  // InDir sets the folder in which the command should be executed
   203  func InDir(path string) func(*icmd.Cmd) func() {
   204  	return func(cmd *icmd.Cmd) func() {
   205  		cmd.Dir = path
   206  		return nil
   207  	}
   208  }
   209  
   210  // WithStdout sets the standard output writer of the command
   211  func WithStdout(writer io.Writer) func(*icmd.Cmd) func() {
   212  	return func(cmd *icmd.Cmd) func() {
   213  		cmd.Stdout = writer
   214  		return nil
   215  	}
   216  }
   217  
   218  // WithStdin sets the standard input reader for the command
   219  func WithStdin(stdin io.Reader) func(*icmd.Cmd) func() {
   220  	return func(cmd *icmd.Cmd) func() {
   221  		cmd.Stdin = stdin
   222  		return nil
   223  	}
   224  }