github.com/lacework-dev/go-moby@v20.10.12+incompatible/integration-cli/docker_utils_test.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/docker/docker/api/types"
    19  	"github.com/docker/docker/client"
    20  	"github.com/docker/docker/integration-cli/cli"
    21  	"github.com/docker/docker/integration-cli/daemon"
    22  	"gotest.tools/v3/assert"
    23  	"gotest.tools/v3/assert/cmp"
    24  	"gotest.tools/v3/icmd"
    25  	"gotest.tools/v3/poll"
    26  )
    27  
    28  func deleteImages(images ...string) error {
    29  	args := []string{dockerBinary, "rmi", "-f"}
    30  	return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error
    31  }
    32  
    33  // Deprecated: use cli.Docker or cli.DockerCmd
    34  func dockerCmdWithError(args ...string) (string, int, error) {
    35  	result := cli.Docker(cli.Args(args...))
    36  	if result.Error != nil {
    37  		return result.Combined(), result.ExitCode, result.Compare(icmd.Success)
    38  	}
    39  	return result.Combined(), result.ExitCode, result.Error
    40  }
    41  
    42  // Deprecated: use cli.Docker or cli.DockerCmd
    43  func dockerCmd(c testing.TB, args ...string) (string, int) {
    44  	c.Helper()
    45  	result := cli.DockerCmd(c, args...)
    46  	return result.Combined(), result.ExitCode
    47  }
    48  
    49  // Deprecated: use cli.Docker or cli.DockerCmd
    50  func dockerCmdWithResult(args ...string) *icmd.Result {
    51  	return cli.Docker(cli.Args(args...))
    52  }
    53  
    54  func findContainerIP(c *testing.T, id string, network string) string {
    55  	c.Helper()
    56  	out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id)
    57  	return strings.Trim(out, " \r\n'")
    58  }
    59  
    60  func getContainerCount(c *testing.T) int {
    61  	c.Helper()
    62  	const containers = "Containers:"
    63  
    64  	result := icmd.RunCommand(dockerBinary, "info")
    65  	result.Assert(c, icmd.Success)
    66  
    67  	lines := strings.Split(result.Combined(), "\n")
    68  	for _, line := range lines {
    69  		if strings.Contains(line, containers) {
    70  			output := strings.TrimSpace(line)
    71  			output = strings.TrimPrefix(output, containers)
    72  			output = strings.Trim(output, " ")
    73  			containerCount, err := strconv.Atoi(output)
    74  			assert.NilError(c, err)
    75  			return containerCount
    76  		}
    77  	}
    78  	return 0
    79  }
    80  
    81  func inspectFieldAndUnmarshall(c *testing.T, name, field string, output interface{}) {
    82  	c.Helper()
    83  	str := inspectFieldJSON(c, name, field)
    84  	err := json.Unmarshal([]byte(str), output)
    85  	if c != nil {
    86  		assert.Assert(c, err == nil, "failed to unmarshal: %v", err)
    87  	}
    88  }
    89  
    90  // Deprecated: use cli.Inspect
    91  func inspectFilter(name, filter string) (string, error) {
    92  	format := fmt.Sprintf("{{%s}}", filter)
    93  	result := icmd.RunCommand(dockerBinary, "inspect", "-f", format, name)
    94  	if result.Error != nil || result.ExitCode != 0 {
    95  		return "", fmt.Errorf("failed to inspect %s: %s", name, result.Combined())
    96  	}
    97  	return strings.TrimSpace(result.Combined()), nil
    98  }
    99  
   100  // Deprecated: use cli.Inspect
   101  func inspectFieldWithError(name, field string) (string, error) {
   102  	return inspectFilter(name, fmt.Sprintf(".%s", field))
   103  }
   104  
   105  // Deprecated: use cli.Inspect
   106  func inspectField(c *testing.T, name, field string) string {
   107  	c.Helper()
   108  	out, err := inspectFilter(name, fmt.Sprintf(".%s", field))
   109  	if c != nil {
   110  		assert.NilError(c, err)
   111  	}
   112  	return out
   113  }
   114  
   115  // Deprecated: use cli.Inspect
   116  func inspectFieldJSON(c *testing.T, name, field string) string {
   117  	c.Helper()
   118  	out, err := inspectFilter(name, fmt.Sprintf("json .%s", field))
   119  	if c != nil {
   120  		assert.NilError(c, err)
   121  	}
   122  	return out
   123  }
   124  
   125  // Deprecated: use cli.Inspect
   126  func inspectFieldMap(c *testing.T, name, path, field string) string {
   127  	c.Helper()
   128  	out, err := inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
   129  	if c != nil {
   130  		assert.NilError(c, err)
   131  	}
   132  	return out
   133  }
   134  
   135  // Deprecated: use cli.Inspect
   136  func inspectMountSourceField(name, destination string) (string, error) {
   137  	m, err := inspectMountPoint(name, destination)
   138  	if err != nil {
   139  		return "", err
   140  	}
   141  	return m.Source, nil
   142  }
   143  
   144  // Deprecated: use cli.Inspect
   145  func inspectMountPoint(name, destination string) (types.MountPoint, error) {
   146  	out, err := inspectFilter(name, "json .Mounts")
   147  	if err != nil {
   148  		return types.MountPoint{}, err
   149  	}
   150  
   151  	return inspectMountPointJSON(out, destination)
   152  }
   153  
   154  var errMountNotFound = errors.New("mount point not found")
   155  
   156  // Deprecated: use cli.Inspect
   157  func inspectMountPointJSON(j, destination string) (types.MountPoint, error) {
   158  	var mp []types.MountPoint
   159  	if err := json.Unmarshal([]byte(j), &mp); err != nil {
   160  		return types.MountPoint{}, err
   161  	}
   162  
   163  	var m *types.MountPoint
   164  	for _, c := range mp {
   165  		if c.Destination == destination {
   166  			m = &c
   167  			break
   168  		}
   169  	}
   170  
   171  	if m == nil {
   172  		return types.MountPoint{}, errMountNotFound
   173  	}
   174  
   175  	return *m, nil
   176  }
   177  
   178  func getIDByName(c *testing.T, name string) string {
   179  	c.Helper()
   180  	id, err := inspectFieldWithError(name, "Id")
   181  	assert.NilError(c, err)
   182  	return id
   183  }
   184  
   185  // Deprecated: use cli.Build
   186  func buildImageSuccessfully(c *testing.T, name string, cmdOperators ...cli.CmdOperator) {
   187  	c.Helper()
   188  	buildImage(name, cmdOperators...).Assert(c, icmd.Success)
   189  }
   190  
   191  // Deprecated: use cli.Build
   192  func buildImage(name string, cmdOperators ...cli.CmdOperator) *icmd.Result {
   193  	return cli.Docker(cli.Build(name), cmdOperators...)
   194  }
   195  
   196  // Write `content` to the file at path `dst`, creating it if necessary,
   197  // as well as any missing directories.
   198  // The file is truncated if it already exists.
   199  // Fail the test when error occurs.
   200  func writeFile(dst, content string, c *testing.T) {
   201  	c.Helper()
   202  	// Create subdirectories if necessary
   203  	assert.Assert(c, os.MkdirAll(path.Dir(dst), 0700) == nil)
   204  	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
   205  	assert.NilError(c, err)
   206  	defer f.Close()
   207  	// Write content (truncate if it exists)
   208  	_, err = io.Copy(f, strings.NewReader(content))
   209  	assert.NilError(c, err)
   210  }
   211  
   212  // Return the contents of file at path `src`.
   213  // Fail the test when error occurs.
   214  func readFile(src string, c *testing.T) (content string) {
   215  	c.Helper()
   216  	data, err := ioutil.ReadFile(src)
   217  	assert.NilError(c, err)
   218  
   219  	return string(data)
   220  }
   221  
   222  func containerStorageFile(containerID, basename string) string {
   223  	return filepath.Join(testEnv.PlatformDefaults.ContainerStoragePath, containerID, basename)
   224  }
   225  
   226  // docker commands that use this function must be run with the '-d' switch.
   227  func runCommandAndReadContainerFile(c *testing.T, filename string, command string, args ...string) []byte {
   228  	c.Helper()
   229  	result := icmd.RunCommand(command, args...)
   230  	result.Assert(c, icmd.Success)
   231  	contID := strings.TrimSpace(result.Combined())
   232  	if err := waitRun(contID); err != nil {
   233  		c.Fatalf("%v: %q", contID, err)
   234  	}
   235  	return readContainerFile(c, contID, filename)
   236  }
   237  
   238  func readContainerFile(c *testing.T, containerID, filename string) []byte {
   239  	c.Helper()
   240  	f, err := os.Open(containerStorageFile(containerID, filename))
   241  	assert.NilError(c, err)
   242  	defer f.Close()
   243  
   244  	content, err := ioutil.ReadAll(f)
   245  	assert.NilError(c, err)
   246  	return content
   247  }
   248  
   249  func readContainerFileWithExec(c *testing.T, containerID, filename string) []byte {
   250  	c.Helper()
   251  	result := icmd.RunCommand(dockerBinary, "exec", containerID, "cat", filename)
   252  	result.Assert(c, icmd.Success)
   253  	return []byte(result.Combined())
   254  }
   255  
   256  // daemonTime provides the current time on the daemon host
   257  func daemonTime(c *testing.T) time.Time {
   258  	c.Helper()
   259  	if testEnv.IsLocalDaemon() {
   260  		return time.Now()
   261  	}
   262  	cli, err := client.NewClientWithOpts(client.FromEnv)
   263  	assert.NilError(c, err)
   264  	defer cli.Close()
   265  
   266  	info, err := cli.Info(context.Background())
   267  	assert.NilError(c, err)
   268  
   269  	dt, err := time.Parse(time.RFC3339Nano, info.SystemTime)
   270  	assert.Assert(c, err == nil, "invalid time format in GET /info response")
   271  	return dt
   272  }
   273  
   274  // daemonUnixTime returns the current time on the daemon host with nanoseconds precision.
   275  // It return the time formatted how the client sends timestamps to the server.
   276  func daemonUnixTime(c *testing.T) string {
   277  	c.Helper()
   278  	return parseEventTime(daemonTime(c))
   279  }
   280  
   281  func parseEventTime(t time.Time) string {
   282  	return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond()))
   283  }
   284  
   285  // appendBaseEnv appends the minimum set of environment variables to exec the
   286  // docker cli binary for testing with correct configuration to the given env
   287  // list.
   288  func appendBaseEnv(isTLS bool, env ...string) []string {
   289  	preserveList := []string{
   290  		// preserve remote test host
   291  		"DOCKER_HOST",
   292  
   293  		// windows: requires preserving SystemRoot, otherwise dial tcp fails
   294  		// with "GetAddrInfoW: A non-recoverable error occurred during a database lookup."
   295  		"SystemRoot",
   296  
   297  		// testing help text requires the $PATH to dockerd is set
   298  		"PATH",
   299  	}
   300  	if isTLS {
   301  		preserveList = append(preserveList, "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH")
   302  	}
   303  
   304  	for _, key := range preserveList {
   305  		if val := os.Getenv(key); val != "" {
   306  			env = append(env, fmt.Sprintf("%s=%s", key, val))
   307  		}
   308  	}
   309  	return env
   310  }
   311  
   312  func createTmpFile(c *testing.T, content string) string {
   313  	c.Helper()
   314  	f, err := ioutil.TempFile("", "testfile")
   315  	assert.NilError(c, err)
   316  
   317  	filename := f.Name()
   318  
   319  	err = ioutil.WriteFile(filename, []byte(content), 0644)
   320  	assert.NilError(c, err)
   321  
   322  	return filename
   323  }
   324  
   325  // waitRun will wait for the specified container to be running, maximum 5 seconds.
   326  // Deprecated: use cli.WaitFor
   327  func waitRun(contID string) error {
   328  	return waitInspect(contID, "{{.State.Running}}", "true", 5*time.Second)
   329  }
   330  
   331  // waitInspect will wait for the specified container to have the specified string
   332  // in the inspect output. It will wait until the specified timeout (in seconds)
   333  // is reached.
   334  // Deprecated: use cli.WaitFor
   335  func waitInspect(name, expr, expected string, timeout time.Duration) error {
   336  	return waitInspectWithArgs(name, expr, expected, timeout)
   337  }
   338  
   339  // Deprecated: use cli.WaitFor
   340  func waitInspectWithArgs(name, expr, expected string, timeout time.Duration, arg ...string) error {
   341  	return daemon.WaitInspectWithArgs(dockerBinary, name, expr, expected, timeout, arg...)
   342  }
   343  
   344  func getInspectBody(c *testing.T, version, id string) []byte {
   345  	c.Helper()
   346  	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(version))
   347  	assert.NilError(c, err)
   348  	defer cli.Close()
   349  	_, body, err := cli.ContainerInspectWithRaw(context.Background(), id, false)
   350  	assert.NilError(c, err)
   351  	return body
   352  }
   353  
   354  // Run a long running idle task in a background container using the
   355  // system-specific default image and command.
   356  func runSleepingContainer(c *testing.T, extraArgs ...string) string {
   357  	c.Helper()
   358  	return runSleepingContainerInImage(c, "busybox", extraArgs...)
   359  }
   360  
   361  // Run a long running idle task in a background container using the specified
   362  // image and the system-specific command.
   363  func runSleepingContainerInImage(c *testing.T, image string, extraArgs ...string) string {
   364  	c.Helper()
   365  	args := []string{"run", "-d"}
   366  	args = append(args, extraArgs...)
   367  	args = append(args, image)
   368  	args = append(args, sleepCommandForDaemonPlatform()...)
   369  	return strings.TrimSpace(cli.DockerCmd(c, args...).Combined())
   370  }
   371  
   372  // minimalBaseImage returns the name of the minimal base image for the current
   373  // daemon platform.
   374  func minimalBaseImage() string {
   375  	return testEnv.PlatformDefaults.BaseImage
   376  }
   377  
   378  func getGoroutineNumber() (int, error) {
   379  	cli, err := client.NewClientWithOpts(client.FromEnv)
   380  	if err != nil {
   381  		return 0, err
   382  	}
   383  	defer cli.Close()
   384  
   385  	info, err := cli.Info(context.Background())
   386  	if err != nil {
   387  		return 0, err
   388  	}
   389  	return info.NGoroutines, nil
   390  }
   391  
   392  func waitForGoroutines(expected int) error {
   393  	t := time.After(30 * time.Second)
   394  	for {
   395  		select {
   396  		case <-t:
   397  			n, err := getGoroutineNumber()
   398  			if err != nil {
   399  				return err
   400  			}
   401  			if n > expected {
   402  				return fmt.Errorf("leaked goroutines: expected less than or equal to %d, got: %d", expected, n)
   403  			}
   404  		default:
   405  			n, err := getGoroutineNumber()
   406  			if err != nil {
   407  				return err
   408  			}
   409  			if n <= expected {
   410  				return nil
   411  			}
   412  			time.Sleep(200 * time.Millisecond)
   413  		}
   414  	}
   415  }
   416  
   417  // getErrorMessage returns the error message from an error API response
   418  func getErrorMessage(c *testing.T, body []byte) string {
   419  	c.Helper()
   420  	var resp types.ErrorResponse
   421  	assert.Assert(c, json.Unmarshal(body, &resp) == nil)
   422  	return strings.TrimSpace(resp.Message)
   423  }
   424  
   425  type checkF func(*testing.T) (interface{}, string)
   426  type reducer func(...interface{}) interface{}
   427  
   428  func pollCheck(t *testing.T, f checkF, compare func(x interface{}) assert.BoolOrComparison) poll.Check {
   429  	return func(poll.LogT) poll.Result {
   430  		t.Helper()
   431  		v, comment := f(t)
   432  		r := compare(v)
   433  		switch r := r.(type) {
   434  		case bool:
   435  			if r {
   436  				return poll.Success()
   437  			}
   438  		case cmp.Comparison:
   439  			if r().Success() {
   440  				return poll.Success()
   441  			}
   442  		default:
   443  			panic(fmt.Errorf("pollCheck: type %T not implemented", r))
   444  		}
   445  		return poll.Continue(comment)
   446  	}
   447  }
   448  
   449  func reducedCheck(r reducer, funcs ...checkF) checkF {
   450  	return func(c *testing.T) (interface{}, string) {
   451  		c.Helper()
   452  		var values []interface{}
   453  		var comments []string
   454  		for _, f := range funcs {
   455  			v, comment := f(c)
   456  			values = append(values, v)
   457  			if len(comment) > 0 {
   458  				comments = append(comments, comment)
   459  			}
   460  		}
   461  		return r(values...), fmt.Sprintf("%v", strings.Join(comments, ", "))
   462  	}
   463  }
   464  
   465  func sumAsIntegers(vals ...interface{}) interface{} {
   466  	var s int
   467  	for _, v := range vals {
   468  		s += v.(int)
   469  	}
   470  	return s
   471  }