github.com/alloyci/alloy-runner@v1.0.1-0.20180222164613-925503ccafd6/executors/docker/executor_docker_command_test.go (about)

     1  package docker_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"net/url"
     8  	"os"
     9  	"os/exec"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/hashicorp/go-version"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"gitlab.com/gitlab-org/gitlab-runner/common"
    20  	"gitlab.com/gitlab-org/gitlab-runner/executors/docker"
    21  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    22  	"gitlab.com/gitlab-org/gitlab-runner/helpers/docker"
    23  )
    24  
    25  func TestDockerCommandSuccessRun(t *testing.T) {
    26  	if helpers.SkipIntegrationTests(t, "docker", "info") {
    27  		return
    28  	}
    29  
    30  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
    31  	assert.NoError(t, err)
    32  	build := &common.Build{
    33  		JobResponse: successfulBuild,
    34  		Runner: &common.RunnerConfig{
    35  			RunnerSettings: common.RunnerSettings{
    36  				Executor: "docker",
    37  				Docker: &common.DockerConfig{
    38  					Image: "alpine",
    39  				},
    40  			},
    41  		},
    42  	}
    43  
    44  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
    45  	assert.NoError(t, err)
    46  }
    47  
    48  func TestDockerCommandNoRootImage(t *testing.T) {
    49  	if helpers.SkipIntegrationTests(t, "docker", "info") {
    50  		return
    51  	}
    52  
    53  	successfulBuild, err := common.GetRemoteSuccessfulBuildWithDumpedVariables()
    54  
    55  	assert.NoError(t, err)
    56  	successfulBuild.Image.Name = "registry.gitlab.com/gitlab-org/gitlab-runner/alpine-no-root"
    57  	build := &common.Build{
    58  		JobResponse: successfulBuild,
    59  		Runner: &common.RunnerConfig{
    60  			RunnerSettings: common.RunnerSettings{
    61  				Executor: "docker",
    62  				Docker:   &common.DockerConfig{},
    63  			},
    64  		},
    65  	}
    66  
    67  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
    68  	assert.NoError(t, err)
    69  }
    70  
    71  func TestDockerCommandBuildFail(t *testing.T) {
    72  	if helpers.SkipIntegrationTests(t, "docker", "info") {
    73  		return
    74  	}
    75  
    76  	failedBuild, err := common.GetRemoteFailedBuild()
    77  	assert.NoError(t, err)
    78  	build := &common.Build{
    79  		JobResponse: failedBuild,
    80  		Runner: &common.RunnerConfig{
    81  			RunnerSettings: common.RunnerSettings{
    82  				Executor: "docker",
    83  				Docker: &common.DockerConfig{
    84  					Image: "alpine",
    85  				},
    86  			},
    87  		},
    88  	}
    89  
    90  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
    91  	require.Error(t, err, "error")
    92  	assert.IsType(t, err, &common.BuildError{})
    93  	assert.Contains(t, err.Error(), "exit code 1")
    94  }
    95  
    96  func TestDockerCommandWithAllowedImagesRun(t *testing.T) {
    97  	if helpers.SkipIntegrationTests(t, "docker", "info") {
    98  		return
    99  	}
   100  
   101  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   102  	successfulBuild.Image = common.Image{Name: "$IMAGE_NAME"}
   103  	successfulBuild.Variables = append(successfulBuild.Variables, common.JobVariable{
   104  		Key:      "IMAGE_NAME",
   105  		Value:    "alpine",
   106  		Public:   true,
   107  		Internal: false,
   108  		File:     false,
   109  	})
   110  	successfulBuild.Services = append(successfulBuild.Services, common.Image{Name: "docker:dind"})
   111  	assert.NoError(t, err)
   112  	build := &common.Build{
   113  		JobResponse: successfulBuild,
   114  		Runner: &common.RunnerConfig{
   115  			RunnerSettings: common.RunnerSettings{
   116  				Executor: "docker",
   117  				Docker: &common.DockerConfig{
   118  					AllowedImages:   []string{"alpine"},
   119  					AllowedServices: []string{"docker:dind"},
   120  					Privileged:      true,
   121  				},
   122  			},
   123  		},
   124  	}
   125  
   126  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   127  	assert.NoError(t, err)
   128  }
   129  
   130  func isDockerOlderThan17_07(t *testing.T) bool {
   131  	client, err := docker_helpers.New(
   132  		docker_helpers.DockerCredentials{}, docker.DockerAPIVersion)
   133  	require.NoError(t, err, "should be able to connect to docker")
   134  
   135  	types, err := client.Info(context.Background())
   136  	require.NoError(t, err, "should be able to get docker info")
   137  
   138  	localVersion, err := version.NewVersion(types.ServerVersion)
   139  	require.NoError(t, err)
   140  
   141  	checkedVersion, err := version.NewVersion("17.07.0-ce")
   142  	require.NoError(t, err)
   143  
   144  	return localVersion.LessThan(checkedVersion)
   145  }
   146  
   147  func TestDockerCommandMissingImage(t *testing.T) {
   148  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   149  		return
   150  	}
   151  
   152  	build := &common.Build{
   153  		Runner: &common.RunnerConfig{
   154  			RunnerSettings: common.RunnerSettings{
   155  				Executor: "docker",
   156  				Docker: &common.DockerConfig{
   157  					Image: "some/non-existing/image",
   158  				},
   159  			},
   160  		},
   161  	}
   162  
   163  	err := build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   164  	require.Error(t, err)
   165  	assert.IsType(t, &common.BuildError{}, err)
   166  
   167  	contains := "repository does not exist"
   168  	if isDockerOlderThan17_07(t) {
   169  		contains = "not found"
   170  	}
   171  
   172  	assert.Contains(t, err.Error(), contains)
   173  }
   174  
   175  func TestDockerCommandMissingTag(t *testing.T) {
   176  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   177  		return
   178  	}
   179  
   180  	build := &common.Build{
   181  		Runner: &common.RunnerConfig{
   182  			RunnerSettings: common.RunnerSettings{
   183  				Executor: "docker",
   184  				Docker: &common.DockerConfig{
   185  					Image: "docker:missing-tag",
   186  				},
   187  			},
   188  		},
   189  	}
   190  
   191  	err := build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   192  	require.Error(t, err)
   193  	assert.IsType(t, &common.BuildError{}, err)
   194  	assert.Contains(t, err.Error(), "not found")
   195  }
   196  
   197  func TestDockerCommandBuildAbort(t *testing.T) {
   198  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   199  		return
   200  	}
   201  
   202  	longRunningBuild, err := common.GetRemoteLongRunningBuild()
   203  	assert.NoError(t, err)
   204  	build := &common.Build{
   205  		JobResponse: longRunningBuild,
   206  		Runner: &common.RunnerConfig{
   207  			RunnerSettings: common.RunnerSettings{
   208  				Executor: "docker",
   209  				Docker: &common.DockerConfig{
   210  					Image: "alpine",
   211  				},
   212  			},
   213  		},
   214  		SystemInterrupt: make(chan os.Signal, 1),
   215  	}
   216  
   217  	abortTimer := time.AfterFunc(time.Second, func() {
   218  		t.Log("Interrupt")
   219  		build.SystemInterrupt <- os.Interrupt
   220  	})
   221  	defer abortTimer.Stop()
   222  
   223  	timeoutTimer := time.AfterFunc(time.Minute, func() {
   224  		t.Log("Timedout")
   225  		t.FailNow()
   226  	})
   227  	defer timeoutTimer.Stop()
   228  
   229  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   230  	assert.EqualError(t, err, "aborted: interrupt")
   231  }
   232  
   233  func TestDockerCommandBuildCancel(t *testing.T) {
   234  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   235  		return
   236  	}
   237  
   238  	longRunningBuild, err := common.GetRemoteLongRunningBuild()
   239  	assert.NoError(t, err)
   240  	build := &common.Build{
   241  		JobResponse: longRunningBuild,
   242  		Runner: &common.RunnerConfig{
   243  			RunnerSettings: common.RunnerSettings{
   244  				Executor: "docker",
   245  				Docker: &common.DockerConfig{
   246  					Image: "alpine",
   247  				},
   248  			},
   249  		},
   250  	}
   251  
   252  	trace := &common.Trace{Writer: os.Stdout}
   253  
   254  	abortTimer := time.AfterFunc(time.Second, func() {
   255  		t.Log("Interrupt")
   256  		trace.CancelFunc()
   257  	})
   258  	defer abortTimer.Stop()
   259  
   260  	timeoutTimer := time.AfterFunc(time.Minute, func() {
   261  		t.Log("Timedout")
   262  		t.FailNow()
   263  	})
   264  	defer timeoutTimer.Stop()
   265  
   266  	err = build.Run(&common.Config{}, trace)
   267  	assert.IsType(t, err, &common.BuildError{})
   268  	assert.EqualError(t, err, "canceled")
   269  }
   270  
   271  func TestDockerCommandTwoServicesFromOneImage(t *testing.T) {
   272  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   273  		return
   274  	}
   275  
   276  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   277  	successfulBuild.Services = common.Services{
   278  		{Name: "alpine", Alias: "service-1"},
   279  		{Name: "alpine", Alias: "service-2"},
   280  	}
   281  	assert.NoError(t, err)
   282  	build := &common.Build{
   283  		JobResponse: successfulBuild,
   284  		Runner: &common.RunnerConfig{
   285  			RunnerSettings: common.RunnerSettings{
   286  				Executor: "docker",
   287  				Docker: &common.DockerConfig{
   288  					Image: "alpine",
   289  				},
   290  			},
   291  		},
   292  	}
   293  
   294  	var buffer bytes.Buffer
   295  
   296  	err = build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   297  	assert.NoError(t, err)
   298  	str := buffer.String()
   299  
   300  	re, err := regexp.Compile("(?m)Conflict. The container name [^ ]+ is already in use by container")
   301  	require.NoError(t, err)
   302  	assert.NotRegexp(t, re, str, "Both service containers should be started and use different name")
   303  }
   304  
   305  func TestDockerCommandOutput(t *testing.T) {
   306  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   307  		return
   308  	}
   309  
   310  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   311  	assert.NoError(t, err)
   312  	build := &common.Build{
   313  		JobResponse: successfulBuild,
   314  		Runner: &common.RunnerConfig{
   315  			RunnerSettings: common.RunnerSettings{
   316  				Executor: "docker",
   317  				Docker: &common.DockerConfig{
   318  					Image: "alpine",
   319  				},
   320  			},
   321  		},
   322  	}
   323  
   324  	var buffer bytes.Buffer
   325  
   326  	err = build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   327  	assert.NoError(t, err)
   328  
   329  	re, err := regexp.Compile("(?m)^Cloning into '/builds/gitlab-org/gitlab-test'...")
   330  	assert.NoError(t, err)
   331  	assert.Regexp(t, re, buffer.String())
   332  }
   333  
   334  func TestDockerPrivilegedServiceAccessingBuildsFolder(t *testing.T) {
   335  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   336  		return
   337  	}
   338  
   339  	commands := []string{
   340  		"docker info",
   341  		"docker run -v $(pwd):$(pwd) -w $(pwd) alpine touch test",
   342  		"cat test",
   343  	}
   344  
   345  	strategies := []string{
   346  		"fetch",
   347  		"clone",
   348  	}
   349  
   350  	for _, strategy := range strategies {
   351  		t.Log("Testing", strategy, "strategy...")
   352  		longRunningBuild, err := common.GetRemoteLongRunningBuild()
   353  		assert.NoError(t, err)
   354  		build := &common.Build{
   355  			JobResponse: longRunningBuild,
   356  			Runner: &common.RunnerConfig{
   357  				RunnerSettings: common.RunnerSettings{
   358  					Executor: "docker",
   359  					Docker: &common.DockerConfig{
   360  						Image:      "alpine",
   361  						Privileged: true,
   362  					},
   363  				},
   364  			},
   365  		}
   366  		build.Steps = common.Steps{
   367  			common.Step{
   368  				Name:         common.StepNameScript,
   369  				Script:       common.StepScript(commands),
   370  				When:         common.StepWhenOnSuccess,
   371  				AllowFailure: false,
   372  			},
   373  		}
   374  		build.Image.Name = "docker:git"
   375  		build.Services = common.Services{
   376  			common.Image{
   377  				Name: "docker:dind",
   378  			},
   379  		}
   380  		build.Variables = append(build.Variables, common.JobVariable{
   381  			Key: "GIT_STRATEGY", Value: strategy,
   382  		})
   383  
   384  		err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   385  		assert.NoError(t, err)
   386  	}
   387  }
   388  
   389  func getTestDockerJob(t *testing.T) *common.Build {
   390  	commands := []string{
   391  		"docker info",
   392  	}
   393  
   394  	longRunningBuild, err := common.GetRemoteLongRunningBuild()
   395  	assert.NoError(t, err)
   396  
   397  	build := &common.Build{
   398  		JobResponse: longRunningBuild,
   399  		Runner: &common.RunnerConfig{
   400  			RunnerSettings: common.RunnerSettings{
   401  				Executor: "docker",
   402  				Docker: &common.DockerConfig{
   403  					Image:      "alpine",
   404  					Privileged: true,
   405  				},
   406  			},
   407  		},
   408  	}
   409  	build.Steps = common.Steps{
   410  		common.Step{
   411  			Name:         common.StepNameScript,
   412  			Script:       common.StepScript(commands),
   413  			When:         common.StepWhenOnSuccess,
   414  			AllowFailure: false,
   415  		},
   416  	}
   417  
   418  	return build
   419  }
   420  
   421  func TestDockerExtendedConfigurationFromJob(t *testing.T) {
   422  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   423  		return
   424  	}
   425  
   426  	examples := []struct {
   427  		image     common.Image
   428  		services  common.Services
   429  		variables common.JobVariables
   430  	}{
   431  		{
   432  			image: common.Image{
   433  				Name:       "$IMAGE_NAME",
   434  				Entrypoint: []string{"sh", "-c"},
   435  			},
   436  			services: common.Services{
   437  				common.Image{
   438  					Name:       "$SERVICE_NAME",
   439  					Entrypoint: []string{"sh", "-c"},
   440  					Command:    []string{"dockerd-entrypoint.sh"},
   441  					Alias:      "my-docker-service",
   442  				},
   443  			},
   444  			variables: common.JobVariables{
   445  				{Key: "DOCKER_HOST", Value: "tcp://my-docker-service:2375"},
   446  				{Key: "IMAGE_NAME", Value: "docker:git"},
   447  				{Key: "SERVICE_NAME", Value: "docker:dind"},
   448  			},
   449  		},
   450  		{
   451  			image: common.Image{
   452  				Name: "$IMAGE_NAME",
   453  			},
   454  			services: common.Services{
   455  				common.Image{
   456  					Name: "$SERVICE_NAME",
   457  				},
   458  			},
   459  			variables: common.JobVariables{
   460  				{Key: "DOCKER_HOST", Value: "tcp://docker:2375"},
   461  				{Key: "IMAGE_NAME", Value: "docker:git"},
   462  				{Key: "SERVICE_NAME", Value: "docker:dind"},
   463  			},
   464  		},
   465  	}
   466  
   467  	for exampleID, example := range examples {
   468  		t.Run(fmt.Sprintf("example-%d", exampleID), func(t *testing.T) {
   469  			build := getTestDockerJob(t)
   470  			build.Image = example.image
   471  			build.Services = example.services
   472  			build.Variables = append(build.Variables, example.variables...)
   473  
   474  			err := build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   475  			assert.NoError(t, err)
   476  		})
   477  	}
   478  }
   479  
   480  func runTestJobWithOutput(t *testing.T, build *common.Build) (output string) {
   481  	var buffer bytes.Buffer
   482  
   483  	err := build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   484  	assert.NoError(t, err)
   485  
   486  	output = buffer.String()
   487  	return
   488  }
   489  
   490  func TestCacheInContainer(t *testing.T) {
   491  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   492  		return
   493  	}
   494  
   495  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   496  	assert.NoError(t, err)
   497  
   498  	successfulBuild.JobInfo.ProjectID = int(time.Now().Unix())
   499  	successfulBuild.Steps[0].Script = common.StepScript{
   500  		"(test -d cached/ && ls -lh cached/) || echo \"no cached directory\"",
   501  		"(test -f cached/date && cat cached/date) || echo \"no cached date\"",
   502  		"mkdir -p cached",
   503  		"date > cached/date",
   504  	}
   505  	successfulBuild.Cache = common.Caches{
   506  		common.Cache{
   507  			Key:    "key",
   508  			Paths:  common.ArtifactPaths{"cached/*"},
   509  			Policy: common.CachePolicyPullPush,
   510  		},
   511  	}
   512  
   513  	build := &common.Build{
   514  		JobResponse: successfulBuild,
   515  		Runner: &common.RunnerConfig{
   516  			RunnerSettings: common.RunnerSettings{
   517  				Executor: "docker",
   518  				Docker: &common.DockerConfig{
   519  					Image:   "alpine",
   520  					Volumes: []string{"/cache"},
   521  				},
   522  			},
   523  		},
   524  	}
   525  
   526  	cacheNotPresentRE := regexp.MustCompile("(?m)^no cached directory")
   527  	skipCacheDownload := "Not downloading cache key due to policy"
   528  	skipCacheUpload := "Not uploading cache key due to policy"
   529  
   530  	// The first job lacks any cache to pull, but tries to both pull and push
   531  	output := runTestJobWithOutput(t, build)
   532  	assert.Regexp(t, cacheNotPresentRE, output, "First job execution should not have cached data")
   533  	assert.NotContains(t, output, skipCacheDownload, "Cache download should be performed with policy: %s", common.CachePolicyPullPush)
   534  	assert.NotContains(t, output, skipCacheUpload, "Cache upload should be performed with policy: %s", common.CachePolicyPullPush)
   535  
   536  	// pull-only jobs should skip the push step
   537  	build.JobResponse.Cache[0].Policy = common.CachePolicyPull
   538  	output = runTestJobWithOutput(t, build)
   539  	assert.NotRegexp(t, cacheNotPresentRE, output, "Second job execution should have cached data")
   540  	assert.NotContains(t, output, skipCacheDownload, "Cache download should be performed with policy: %s", common.CachePolicyPull)
   541  	assert.Contains(t, output, skipCacheUpload, "Cache upload should be skipped with policy: %s", common.CachePolicyPull)
   542  
   543  	// push-only jobs should skip the pull step
   544  	build.JobResponse.Cache[0].Policy = common.CachePolicyPush
   545  	output = runTestJobWithOutput(t, build)
   546  	assert.Regexp(t, cacheNotPresentRE, output, "Third job execution should not have cached data")
   547  	assert.Contains(t, output, skipCacheDownload, "Cache download be skipped with policy: push")
   548  	assert.NotContains(t, output, skipCacheUpload, "Cache upload should be performed with policy: push")
   549  }
   550  
   551  func TestDockerImageNameFromVariable(t *testing.T) {
   552  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   553  		return
   554  	}
   555  
   556  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   557  	successfulBuild.Variables = append(successfulBuild.Variables, common.JobVariable{
   558  		Key:   "CI_REGISTRY_IMAGE",
   559  		Value: "alpine",
   560  	})
   561  	successfulBuild.Image = common.Image{
   562  		Name: "$CI_REGISTRY_IMAGE",
   563  	}
   564  	assert.NoError(t, err)
   565  	build := &common.Build{
   566  		JobResponse: successfulBuild,
   567  		Runner: &common.RunnerConfig{
   568  			RunnerSettings: common.RunnerSettings{
   569  				Executor: "docker",
   570  				Docker: &common.DockerConfig{
   571  					Image:           "alpine",
   572  					AllowedServices: []string{"alpine"},
   573  				},
   574  			},
   575  		},
   576  	}
   577  
   578  	re := regexp.MustCompile("(?m)^ERROR: The [^ ]+ is not present on list of allowed images")
   579  
   580  	output := runTestJobWithOutput(t, build)
   581  	assert.NotRegexp(t, re, output, "Image's name should be expanded from variable")
   582  }
   583  
   584  func TestDockerServiceNameFromVariable(t *testing.T) {
   585  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   586  		return
   587  	}
   588  
   589  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   590  	successfulBuild.Variables = append(successfulBuild.Variables, common.JobVariable{
   591  		Key:   "CI_REGISTRY_IMAGE",
   592  		Value: "alpine",
   593  	})
   594  	successfulBuild.Services = append(successfulBuild.Services, common.Image{
   595  		Name: "$CI_REGISTRY_IMAGE",
   596  	})
   597  	assert.NoError(t, err)
   598  	build := &common.Build{
   599  		JobResponse: successfulBuild,
   600  		Runner: &common.RunnerConfig{
   601  			RunnerSettings: common.RunnerSettings{
   602  				Executor: "docker",
   603  				Docker: &common.DockerConfig{
   604  					Image:           "alpine",
   605  					AllowedServices: []string{"alpine"},
   606  				},
   607  			},
   608  		},
   609  	}
   610  
   611  	re := regexp.MustCompile("(?m)^ERROR: The [^ ]+ is not present on list of allowed services")
   612  
   613  	output := runTestJobWithOutput(t, build)
   614  	assert.NotRegexp(t, re, output, "Service's name should be expanded from variable")
   615  }
   616  
   617  func runDockerInDocker(version string) (id string, err error) {
   618  	cmd := exec.Command("docker", "run", "--detach", "--privileged", "-p", "2375", "docker:"+version+"-dind")
   619  	cmd.Stderr = os.Stderr
   620  	data, err := cmd.Output()
   621  	if err != nil {
   622  		return
   623  	}
   624  	id = strings.TrimSpace(string(data))
   625  	return
   626  }
   627  
   628  func getDockerCredentials(id string) (credentials docker_helpers.DockerCredentials, err error) {
   629  	cmd := exec.Command("docker", "port", id, "2375")
   630  	cmd.Stderr = os.Stderr
   631  	data, err := cmd.Output()
   632  	if err != nil {
   633  		return
   634  	}
   635  
   636  	hostPort := strings.Split(strings.TrimSpace(string(data)), ":")
   637  	if dockerHost, err := url.Parse(os.Getenv("DOCKER_HOST")); err == nil {
   638  		dockerHostPort := strings.Split(dockerHost.Host, ":")
   639  		hostPort[0] = dockerHostPort[0]
   640  	} else if hostPort[0] == "0.0.0.0" {
   641  		hostPort[0] = "localhost"
   642  	}
   643  	credentials.Host = "tcp://" + hostPort[0] + ":" + hostPort[1]
   644  	return
   645  }
   646  
   647  func waitForDocker(credentials docker_helpers.DockerCredentials) error {
   648  	client, err := docker_helpers.New(credentials, docker.DockerAPIVersion)
   649  	if err != nil {
   650  		return err
   651  	}
   652  
   653  	for i := 0; i < 20; i++ {
   654  		_, err = client.Info(context.Background())
   655  		if err == nil {
   656  			break
   657  		}
   658  		time.Sleep(time.Second)
   659  	}
   660  	return err
   661  }
   662  
   663  func testDockerVersion(t *testing.T, version string) {
   664  	t.Log("Running docker", version, "...")
   665  	id, err := runDockerInDocker(version)
   666  	if err != nil {
   667  		t.Error("Docker run:", err)
   668  		return
   669  	}
   670  
   671  	defer func() {
   672  		exec.Command("docker", "rm", "-f", "-v", id).Run()
   673  	}()
   674  
   675  	t.Log("Getting address of", version, "...")
   676  	credentials, err := getDockerCredentials(id)
   677  	if err != nil {
   678  		t.Error("Docker credentials:", err)
   679  		return
   680  	}
   681  
   682  	t.Log("Connecting to", credentials.Host, "...")
   683  	err = waitForDocker(credentials)
   684  	if err != nil {
   685  		t.Error("Wait for docker:", err)
   686  		return
   687  	}
   688  
   689  	t.Log("Docker", version, "is running at", credentials.Host)
   690  
   691  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   692  	assert.NoError(t, err)
   693  	build := &common.Build{
   694  		JobResponse: successfulBuild,
   695  		Runner: &common.RunnerConfig{
   696  			RunnerSettings: common.RunnerSettings{
   697  				Executor: "docker",
   698  				Docker: &common.DockerConfig{
   699  					Image:             "alpine",
   700  					DockerCredentials: credentials,
   701  					CPUS:              "0.1",
   702  				},
   703  			},
   704  		},
   705  	}
   706  
   707  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   708  	assert.NoError(t, err)
   709  }
   710  
   711  func TestDocker1_8Compatibility(t *testing.T) {
   712  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   713  		return
   714  	}
   715  	if os.Getenv("CI") != "" {
   716  		t.Skip("This test doesn't work in nested dind")
   717  		return
   718  	}
   719  
   720  	testDockerVersion(t, "1.8")
   721  }
   722  
   723  func TestDocker1_9Compatibility(t *testing.T) {
   724  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   725  		return
   726  	}
   727  	if os.Getenv("CI") != "" {
   728  		t.Skip("This test doesn't work in nested dind")
   729  		return
   730  	}
   731  
   732  	testDockerVersion(t, "1.9")
   733  }
   734  
   735  func TestDocker1_10Compatibility(t *testing.T) {
   736  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   737  		return
   738  	}
   739  	if os.Getenv("CI") != "" {
   740  		t.Skip("This test doesn't work in nested dind")
   741  		return
   742  	}
   743  
   744  	testDockerVersion(t, "1.10")
   745  }
   746  
   747  func TestDocker1_11Compatibility(t *testing.T) {
   748  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   749  		return
   750  	}
   751  	if os.Getenv("CI") != "" {
   752  		t.Skip("This test doesn't work in nested dind")
   753  		return
   754  	}
   755  
   756  	testDockerVersion(t, "1.11")
   757  }
   758  
   759  func TestDocker1_12Compatibility(t *testing.T) {
   760  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   761  		return
   762  	}
   763  	if os.Getenv("CI") != "" {
   764  		t.Skip("This test doesn't work in nested dind")
   765  		return
   766  	}
   767  
   768  	testDockerVersion(t, "1.12")
   769  }
   770  
   771  func TestDocker1_13Compatibility(t *testing.T) {
   772  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   773  		return
   774  	}
   775  	if os.Getenv("CI") != "" {
   776  		t.Skip("This test doesn't work in nested dind")
   777  		return
   778  	}
   779  
   780  	testDockerVersion(t, "1.13")
   781  }
   782  
   783  func TestDockerCommandWithBrokenGitSSLCAInfo(t *testing.T) {
   784  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   785  		return
   786  	}
   787  
   788  	successfulBuild, err := common.GetRemoteBrokenTLSBuild()
   789  	assert.NoError(t, err)
   790  	build := &common.Build{
   791  		JobResponse: successfulBuild,
   792  		Runner: &common.RunnerConfig{
   793  			RunnerCredentials: common.RunnerCredentials{
   794  				URL: "https://gitlab.com",
   795  			},
   796  			RunnerSettings: common.RunnerSettings{
   797  				Executor: "docker",
   798  				Docker: &common.DockerConfig{
   799  					Image: "alpine",
   800  				},
   801  			},
   802  		},
   803  	}
   804  
   805  	var buffer bytes.Buffer
   806  
   807  	err = build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   808  	assert.Error(t, err)
   809  	out := buffer.String()
   810  	assert.Contains(t, out, "Cloning repository")
   811  	assert.NotContains(t, out, "Updating/initializing submodules")
   812  }
   813  
   814  func TestDockerCommandWithGitSSLCAInfo(t *testing.T) {
   815  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   816  		return
   817  	}
   818  
   819  	successfulBuild, err := common.GetRemoteGitLabComTLSBuild()
   820  	assert.NoError(t, err)
   821  	build := &common.Build{
   822  		JobResponse: successfulBuild,
   823  		Runner: &common.RunnerConfig{
   824  			RunnerCredentials: common.RunnerCredentials{
   825  				URL: "https://gitlab.com",
   826  			},
   827  			RunnerSettings: common.RunnerSettings{
   828  				Executor: "docker",
   829  				Docker: &common.DockerConfig{
   830  					Image: "alpine",
   831  				},
   832  			},
   833  		},
   834  	}
   835  
   836  	var buffer bytes.Buffer
   837  
   838  	err = build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   839  	assert.NoError(t, err)
   840  	out := buffer.String()
   841  	assert.Contains(t, out, "Cloning repository")
   842  	assert.Contains(t, out, "Updating/initializing submodules")
   843  }
   844  
   845  func TestDockerCommandWithHelperImageConfig(t *testing.T) {
   846  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   847  		return
   848  	}
   849  
   850  	helperImageConfig := "gitlab/gitlab-runner-helper:x86_64-64eea86c"
   851  
   852  	successfulBuild, err := common.GetRemoteSuccessfulBuild()
   853  	assert.NoError(t, err)
   854  	build := &common.Build{
   855  		JobResponse: successfulBuild,
   856  		Runner: &common.RunnerConfig{
   857  			RunnerSettings: common.RunnerSettings{
   858  				Executor: "docker",
   859  				Docker: &common.DockerConfig{
   860  					Image:       "alpine",
   861  					HelperImage: helperImageConfig,
   862  				},
   863  			},
   864  		},
   865  	}
   866  
   867  	var buffer bytes.Buffer
   868  	err = build.Run(&common.Config{}, &common.Trace{Writer: &buffer})
   869  	assert.NoError(t, err)
   870  	out := buffer.String()
   871  	assert.Contains(t, out, "Pulling docker image "+helperImageConfig)
   872  	assert.Contains(t, out, "Using docker image sha256:bbd86c6ba107ae2feb8dbf9024df4b48597c44e1b584a3d901bba91f7fc500e3 for gitlab/gitlab-runner-helper:x86_64-64eea86c ...")
   873  }
   874  
   875  func TestDockerCommandWithDoingPruneAndAfterScript(t *testing.T) {
   876  	if helpers.SkipIntegrationTests(t, "docker", "info") {
   877  		return
   878  	}
   879  
   880  	successfulBuild, err := common.GetRemoteSuccessfulBuildWithAfterScript()
   881  
   882  	// This scripts removes self-created containers that do exit
   883  	// It will fail if: cannot be removed, or no containers is found
   884  	// It is assuming that name of each runner created container starts
   885  	// with `runner-doprune-`
   886  	successfulBuild.Steps[0].Script = common.StepScript{
   887  		"docker ps -a -f status=exited | grep runner-doprune-",
   888  		"docker rm $(docker ps -a -f status=exited | grep runner-doprune- | awk '{print $1}')",
   889  	}
   890  
   891  	assert.NoError(t, err)
   892  	build := &common.Build{
   893  		JobResponse: successfulBuild,
   894  		Runner: &common.RunnerConfig{
   895  			RunnerCredentials: common.RunnerCredentials{
   896  				Token: "doprune",
   897  			},
   898  			RunnerSettings: common.RunnerSettings{
   899  				Executor: "docker",
   900  				Docker: &common.DockerConfig{
   901  					Image: "docker:git",
   902  					Volumes: []string{
   903  						"/var/run/docker.sock:/var/run/docker.sock",
   904  					},
   905  				},
   906  			},
   907  		},
   908  	}
   909  
   910  	err = build.Run(&common.Config{}, &common.Trace{Writer: os.Stdout})
   911  	assert.NoError(t, err)
   912  }