github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/integration-cli/cli/cli.go (about)

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