github.com/nak3/source-to-image@v1.1.10-0.20180319140719-2ed55639898d/test/integration/integration_test.go (about)

     1  // +build integration
     2  
     3  package integration
     4  
     5  import (
     6  	"encoding/json"
     7  	"flag"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"os"
    14  	"path/filepath"
    15  	"runtime"
    16  	"testing"
    17  	"time"
    18  
    19  	dockertypes "github.com/docker/docker/api/types"
    20  	dockercontainer "github.com/docker/docker/api/types/container"
    21  	dockerapi "github.com/docker/docker/client"
    22  	"github.com/golang/glog"
    23  	"github.com/openshift/source-to-image/pkg/api"
    24  	"github.com/openshift/source-to-image/pkg/build/strategies"
    25  	"github.com/openshift/source-to-image/pkg/docker"
    26  	dockerpkg "github.com/openshift/source-to-image/pkg/docker"
    27  	"github.com/openshift/source-to-image/pkg/scm/git"
    28  	"github.com/openshift/source-to-image/pkg/tar"
    29  	"github.com/openshift/source-to-image/pkg/util"
    30  	"github.com/openshift/source-to-image/pkg/util/fs"
    31  	"golang.org/x/net/context"
    32  )
    33  
    34  const (
    35  	DefaultDockerSocket = "unix:///var/run/docker.sock"
    36  	TestSource          = "https://github.com/openshift/ruby-hello-world"
    37  
    38  	FakeBuilderImage                = "sti_test/sti-fake"
    39  	FakeUserImage                   = "sti_test/sti-fake-user"
    40  	FakeImageScripts                = "sti_test/sti-fake-scripts"
    41  	FakeImageScriptsNoSaveArtifacts = "sti_test/sti-fake-scripts-no-save-artifacts"
    42  	FakeImageNoTar                  = "sti_test/sti-fake-no-tar"
    43  	FakeImageOnBuild                = "sti_test/sti-fake-onbuild"
    44  	FakeNumericUserImage            = "sti_test/sti-fake-numericuser"
    45  	FakeImageOnBuildRootUser        = "sti_test/sti-fake-onbuild-rootuser"
    46  	FakeImageOnBuildNumericUser     = "sti_test/sti-fake-onbuild-numericuser"
    47  
    48  	TagCleanBuild                              = "test/sti-fake-app"
    49  	TagCleanBuildUser                          = "test/sti-fake-app-user"
    50  	TagIncrementalBuild                        = "test/sti-incremental-app"
    51  	TagIncrementalBuildUser                    = "test/sti-incremental-app-user"
    52  	TagCleanBuildScripts                       = "test/sti-fake-app-scripts"
    53  	TagIncrementalBuildScripts                 = "test/sti-incremental-app-scripts"
    54  	TagIncrementalBuildScriptsNoSaveArtifacts  = "test/sti-incremental-app-scripts-no-save-artifacts"
    55  	TagCleanLayeredBuildNoTar                  = "test/sti-fake-no-tar"
    56  	TagCleanBuildOnBuild                       = "test/sti-fake-app-onbuild"
    57  	TagIncrementalBuildOnBuild                 = "test/sti-incremental-app-onbuild"
    58  	TagCleanBuildOnBuildNoName                 = "test/sti-fake-app-onbuild-noname"
    59  	TagCleanBuildNoName                        = "test/sti-fake-app-noname"
    60  	TagCleanLayeredBuildNoTarNoName            = "test/sti-fake-no-tar-noname"
    61  	TagCleanBuildAllowedUIDsNamedUser          = "test/sti-fake-alloweduids-nameduser"
    62  	TagCleanBuildAllowedUIDsNumericUser        = "test/sti-fake-alloweduids-numericuser"
    63  	TagCleanBuildAllowedUIDsOnBuildRoot        = "test/sti-fake-alloweduids-onbuildroot"
    64  	TagCleanBuildAllowedUIDsOnBuildNumericUser = "test/sti-fake-alloweduids-onbuildnumeric"
    65  
    66  	// Need to serve the scripts from local host so any potential changes to the
    67  	// scripts are made available for integration testing.
    68  	//
    69  	// Port 23456 must match the port used in the fake image Dockerfiles
    70  	FakeScriptsHTTPURL = "http://127.0.0.1:23456/.s2i/bin"
    71  )
    72  
    73  var engineClient docker.Client
    74  
    75  func init() {
    76  	var err error
    77  	engineClient, err = docker.NewEngineAPIClient(docker.GetDefaultDockerConfig())
    78  	if err != nil {
    79  		panic(err)
    80  	}
    81  
    82  	// get the full path to this .go file so we can construct the file url
    83  	// using this file's dirname
    84  	_, filename, _, _ := runtime.Caller(0)
    85  	testImagesDir := filepath.Join(filepath.Dir(filename), "scripts")
    86  
    87  	l, err := net.Listen("tcp", ":23456")
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  
    92  	hs := http.Server{Handler: http.FileServer(http.Dir(testImagesDir))}
    93  	hs.SetKeepAlivesEnabled(false)
    94  	go hs.Serve(l)
    95  }
    96  
    97  func getDefaultContext() (context.Context, context.CancelFunc) {
    98  	return context.WithTimeout(context.Background(), 20*time.Second)
    99  }
   100  
   101  // TestInjectionBuild tests the build where we inject files to assemble script.
   102  func TestInjectionBuild(t *testing.T) {
   103  	tempdir, err := ioutil.TempDir("", "s2i-test-dir")
   104  	if err != nil {
   105  		t.Errorf("Unable to create temporary directory: %v", err)
   106  	}
   107  	defer os.RemoveAll(tempdir)
   108  
   109  	err = ioutil.WriteFile(filepath.Join(tempdir, "secret"), []byte("secret"), 0666)
   110  	if err != nil {
   111  		t.Errorf("Unable to write content to temporary injection file: %v", err)
   112  	}
   113  
   114  	integration(t).exerciseInjectionBuild(TagCleanBuild, FakeBuilderImage, []string{
   115  		tempdir + ":/tmp",
   116  		tempdir + ":",
   117  	})
   118  }
   119  
   120  type integrationTest struct {
   121  	t             *testing.T
   122  	setupComplete bool
   123  }
   124  
   125  func (i integrationTest) InspectImage(name string) (*dockertypes.ImageInspect, error) {
   126  	ctx, cancel := getDefaultContext()
   127  	defer cancel()
   128  	resp, _, err := engineClient.ImageInspectWithRaw(ctx, name)
   129  	if err != nil {
   130  		if dockerapi.IsErrImageNotFound(err) {
   131  			return nil, fmt.Errorf("no such image :%q", name)
   132  		}
   133  		return nil, err
   134  	}
   135  	return &resp, nil
   136  }
   137  
   138  var (
   139  	FakeScriptsFileURL string
   140  )
   141  
   142  func getLogLevel() (level int) {
   143  	for level = 5; level >= 0; level-- {
   144  		if glog.V(glog.Level(level)) == true {
   145  			break
   146  		}
   147  	}
   148  	return
   149  }
   150  
   151  // setup sets up integration tests
   152  func (i *integrationTest) setup() {
   153  	if !i.setupComplete {
   154  		// get the full path to this .go file so we can construct the file url
   155  		// using this file's dirname
   156  		_, filename, _, _ := runtime.Caller(0)
   157  		testImagesDir := filepath.Join(filepath.Dir(filename), "scripts")
   158  		FakeScriptsFileURL = "file://" + filepath.ToSlash(filepath.Join(testImagesDir, ".s2i", "bin"))
   159  
   160  		for _, image := range []string{TagCleanBuild, TagCleanBuildUser, TagIncrementalBuild, TagIncrementalBuildUser} {
   161  			ctx, cancel := getDefaultContext()
   162  			engineClient.ImageRemove(ctx, image, dockertypes.ImageRemoveOptions{})
   163  			cancel()
   164  		}
   165  
   166  		i.setupComplete = true
   167  	}
   168  
   169  	from := flag.CommandLine
   170  	if vflag := from.Lookup("v"); vflag != nil {
   171  		// the thing here is that we are looking for the bash -v passed into test-integration.sh (with no value),
   172  		// but for glog (https://github.com/golang/glog/blob/master/glog.go), one specifies
   173  		// the logging level with -v=# (i.e. -v=0 or -v=3 or -v=5).
   174  		// so, for the changes stemming from issue 133, we 'reuse' the bash -v, and set the highest glog level.
   175  		// (if you look at STI's main.go, and setupGlog, it essentially maps glog's -v to --loglevel for use by the sti command)
   176  		//NOTE - passing --loglevel or -v=5 into test-integration.sh does not work
   177  		if getLogLevel() != 5 {
   178  			vflag.Value.Set("5")
   179  			// FIXME currently glog has only option to redirect output to stderr
   180  			// the preferred for STI would be to redirect to stdout
   181  			flag.CommandLine.Set("logtostderr", "true")
   182  		}
   183  	}
   184  }
   185  
   186  func integration(t *testing.T) *integrationTest {
   187  	i := &integrationTest{t: t}
   188  	i.setup()
   189  	return i
   190  }
   191  
   192  // Test a clean build.  The simplest case.
   193  func TestCleanBuild(t *testing.T) {
   194  	integration(t).exerciseCleanBuild(TagCleanBuild, false, FakeBuilderImage, "", true, true, false)
   195  }
   196  
   197  // Test Labels
   198  func TestCleanBuildLabel(t *testing.T) {
   199  	integration(t).exerciseCleanBuild(TagCleanBuild, false, FakeBuilderImage, "", true, true, true)
   200  }
   201  
   202  func TestCleanBuildUser(t *testing.T) {
   203  	integration(t).exerciseCleanBuild(TagCleanBuildUser, false, FakeUserImage, "", true, true, false)
   204  }
   205  
   206  func TestCleanBuildFileScriptsURL(t *testing.T) {
   207  	integration(t).exerciseCleanBuild(TagCleanBuild, false, FakeBuilderImage, FakeScriptsFileURL, true, true, false)
   208  }
   209  
   210  func TestCleanBuildHttpScriptsURL(t *testing.T) {
   211  	integration(t).exerciseCleanBuild(TagCleanBuild, false, FakeBuilderImage, FakeScriptsHTTPURL, true, true, false)
   212  }
   213  
   214  func TestCleanBuildScripts(t *testing.T) {
   215  	integration(t).exerciseCleanBuild(TagCleanBuildScripts, false, FakeImageScripts, "", true, true, false)
   216  }
   217  
   218  func TestLayeredBuildNoTar(t *testing.T) {
   219  	integration(t).exerciseCleanBuild(TagCleanLayeredBuildNoTar, false, FakeImageNoTar, FakeScriptsFileURL, false, true, false)
   220  }
   221  
   222  // Test that a build config with a callbackURL will invoke HTTP endpoint
   223  func TestCleanBuildCallbackInvoked(t *testing.T) {
   224  	integration(t).exerciseCleanBuild(TagCleanBuild, true, FakeBuilderImage, "", true, true, false)
   225  }
   226  
   227  func TestCleanBuildOnBuild(t *testing.T) {
   228  	integration(t).exerciseCleanBuild(TagCleanBuildOnBuild, false, FakeImageOnBuild, "", true, true, false)
   229  }
   230  
   231  func TestCleanBuildOnBuildNoName(t *testing.T) {
   232  	integration(t).exerciseCleanBuild(TagCleanBuildOnBuildNoName, false, FakeImageOnBuild, "", false, false, false)
   233  }
   234  
   235  func TestCleanBuildNoName(t *testing.T) {
   236  	integration(t).exerciseCleanBuild(TagCleanBuildNoName, false, FakeBuilderImage, "", true, false, false)
   237  }
   238  
   239  func TestLayeredBuildNoTarNoName(t *testing.T) {
   240  	integration(t).exerciseCleanBuild(TagCleanLayeredBuildNoTarNoName, false, FakeImageNoTar, FakeScriptsFileURL, false, false, false)
   241  }
   242  
   243  func TestAllowedUIDsNamedUser(t *testing.T) {
   244  	integration(t).exerciseCleanAllowedUIDsBuild(TagCleanBuildAllowedUIDsNamedUser, FakeUserImage, true)
   245  }
   246  
   247  func TestAllowedUIDsNumericUser(t *testing.T) {
   248  	integration(t).exerciseCleanAllowedUIDsBuild(TagCleanBuildAllowedUIDsNumericUser, FakeNumericUserImage, false)
   249  }
   250  
   251  func TestAllowedUIDsOnBuildRootUser(t *testing.T) {
   252  	integration(t).exerciseCleanAllowedUIDsBuild(TagCleanBuildAllowedUIDsNamedUser, FakeImageOnBuildRootUser, true)
   253  }
   254  
   255  func TestAllowedUIDsOnBuildNumericUser(t *testing.T) {
   256  	integration(t).exerciseCleanAllowedUIDsBuild(TagCleanBuildAllowedUIDsNumericUser, FakeImageOnBuildNumericUser, false)
   257  }
   258  
   259  func (i *integrationTest) exerciseCleanAllowedUIDsBuild(tag, imageName string, expectError bool) {
   260  	t := i.t
   261  	config := &api.Config{
   262  		DockerConfig:      docker.GetDefaultDockerConfig(),
   263  		BuilderImage:      imageName,
   264  		BuilderPullPolicy: api.DefaultBuilderPullPolicy,
   265  		Source:            git.MustParse(TestSource),
   266  		Tag:               tag,
   267  		Incremental:       false,
   268  		ScriptsURL:        "",
   269  		ExcludeRegExp:     tar.DefaultExclusionPattern.String(),
   270  	}
   271  	config.AllowedUIDs.Set("1-")
   272  	_, _, err := strategies.GetStrategy(engineClient, config)
   273  	if err != nil && !expectError {
   274  		t.Fatalf("Cannot create a new builder: %v", err)
   275  	}
   276  	if err == nil && expectError {
   277  		t.Fatalf("Did not get an error and was expecting one.")
   278  	}
   279  }
   280  
   281  func (i *integrationTest) exerciseCleanBuild(tag string, verifyCallback bool, imageName string, scriptsURL string, expectImageName bool, setTag bool, checkLabel bool) {
   282  	t := i.t
   283  	callbackURL := ""
   284  	callbackInvoked := false
   285  	callbackHasValidJSON := false
   286  	if verifyCallback {
   287  		handler := func(w http.ResponseWriter, r *http.Request) {
   288  			// we got called
   289  			callbackInvoked = true
   290  			// the header is as expected
   291  			contentType := r.Header["Content-Type"][0]
   292  			callbackHasValidJSON = contentType == "application/json"
   293  			// the request body is as expected
   294  			if callbackHasValidJSON {
   295  				defer r.Body.Close()
   296  				body, _ := ioutil.ReadAll(r.Body)
   297  				type CallbackMessage struct {
   298  					Success bool
   299  					Labels  map[string]string
   300  				}
   301  				var callbackMessage CallbackMessage
   302  				err := json.Unmarshal(body, &callbackMessage)
   303  				callbackHasValidJSON = (err == nil) && callbackMessage.Success && len(callbackMessage.Labels) > 0
   304  			}
   305  		}
   306  		ts := httptest.NewServer(http.HandlerFunc(handler))
   307  		defer ts.Close()
   308  		callbackURL = ts.URL
   309  	}
   310  
   311  	var buildTag string
   312  	if setTag {
   313  		buildTag = tag
   314  	} else {
   315  		buildTag = ""
   316  	}
   317  
   318  	config := &api.Config{
   319  		DockerConfig:      docker.GetDefaultDockerConfig(),
   320  		BuilderImage:      imageName,
   321  		BuilderPullPolicy: api.DefaultBuilderPullPolicy,
   322  		Source:            git.MustParse(TestSource),
   323  		Tag:               buildTag,
   324  		Incremental:       false,
   325  		CallbackURL:       callbackURL,
   326  		ScriptsURL:        scriptsURL,
   327  		ExcludeRegExp:     tar.DefaultExclusionPattern.String(),
   328  	}
   329  
   330  	b, _, err := strategies.GetStrategy(engineClient, config)
   331  	if err != nil {
   332  		t.Fatalf("Cannot create a new builder.")
   333  	}
   334  	resp, err := b.Build(config)
   335  	if err != nil {
   336  		t.Fatalf("An error occurred during the build: %v", err)
   337  	} else if !resp.Success {
   338  		t.Fatalf("The build failed.")
   339  	}
   340  	if callbackInvoked != verifyCallback {
   341  		t.Fatalf("S2I build did not invoke callback")
   342  	}
   343  	if callbackHasValidJSON != verifyCallback {
   344  		t.Fatalf("S2I build did not invoke callback with valid json message")
   345  	}
   346  
   347  	// We restrict this check to only when we are passing tag through the build config
   348  	// since we will not end up with an available tag by that name from build
   349  	if setTag {
   350  		i.checkForImage(tag)
   351  		containerID := i.createContainer(tag)
   352  		i.checkBasicBuildState(containerID, resp.WorkingDir)
   353  
   354  		if checkLabel {
   355  			i.checkForLabel(tag)
   356  		}
   357  
   358  		i.removeContainer(containerID)
   359  	}
   360  
   361  	// Check if we receive back an ImageID when we are expecting to
   362  	if expectImageName && len(resp.ImageID) == 0 {
   363  		t.Fatalf("S2I build did not receive an ImageID in response")
   364  	}
   365  	if !expectImageName && len(resp.ImageID) > 0 {
   366  		t.Fatalf("S2I build received an ImageID in response")
   367  	}
   368  }
   369  
   370  // Test an incremental build.
   371  func TestIncrementalBuildAndRemovePreviousImage(t *testing.T) {
   372  	integration(t).exerciseIncrementalBuild(TagIncrementalBuild, FakeBuilderImage, true, false, false)
   373  }
   374  
   375  func TestIncrementalBuildAndKeepPreviousImage(t *testing.T) {
   376  	integration(t).exerciseIncrementalBuild(TagIncrementalBuild, FakeBuilderImage, false, false, false)
   377  }
   378  
   379  func TestIncrementalBuildUser(t *testing.T) {
   380  	integration(t).exerciseIncrementalBuild(TagIncrementalBuildUser, FakeBuilderImage, true, false, false)
   381  }
   382  
   383  func TestIncrementalBuildScripts(t *testing.T) {
   384  	integration(t).exerciseIncrementalBuild(TagIncrementalBuildScripts, FakeImageScripts, true, false, false)
   385  }
   386  
   387  func TestIncrementalBuildScriptsNoSaveArtifacts(t *testing.T) {
   388  	integration(t).exerciseIncrementalBuild(TagIncrementalBuildScriptsNoSaveArtifacts, FakeImageScriptsNoSaveArtifacts, true, true, false)
   389  }
   390  
   391  func TestIncrementalBuildOnBuild(t *testing.T) {
   392  	integration(t).exerciseIncrementalBuild(TagIncrementalBuildOnBuild, FakeImageOnBuild, false, true, true)
   393  }
   394  
   395  func (i *integrationTest) exerciseInjectionBuild(tag, imageName string, injections []string) {
   396  	t := i.t
   397  
   398  	injectionList := api.VolumeList{}
   399  	for _, i := range injections {
   400  		err := injectionList.Set(i)
   401  		if err != nil {
   402  			t.Errorf("injectionList.Set() failed with error %s\n", err)
   403  		}
   404  	}
   405  	config := &api.Config{
   406  		DockerConfig:      docker.GetDefaultDockerConfig(),
   407  		BuilderImage:      imageName,
   408  		BuilderPullPolicy: api.DefaultBuilderPullPolicy,
   409  		Source:            git.MustParse(TestSource),
   410  		Tag:               tag,
   411  		Injections:        injectionList,
   412  		ExcludeRegExp:     tar.DefaultExclusionPattern.String(),
   413  	}
   414  	builder, _, err := strategies.GetStrategy(engineClient, config)
   415  	if err != nil {
   416  		t.Fatalf("Unable to create builder: %v", err)
   417  	}
   418  	resp, err := builder.Build(config)
   419  	if err != nil {
   420  		t.Fatalf("Unexpected error occurred during build: %v", err)
   421  	}
   422  	if !resp.Success {
   423  		t.Fatalf("S2I build failed.")
   424  	}
   425  	i.checkForImage(tag)
   426  	containerID := i.createContainer(tag)
   427  	defer i.removeContainer(containerID)
   428  
   429  	// Check that the injected file is delivered to assemble script
   430  	i.fileExists(containerID, "/sti-fake/secret-delivered")
   431  	i.fileExists(containerID, "/sti-fake/relative-secret-delivered")
   432  
   433  	// Make sure the injected file does not exists in resulting image
   434  	files, err := util.ExpandInjectedFiles(fs.NewFileSystem(), injectionList)
   435  	if err != nil {
   436  		t.Errorf("Unexpected error: %v", err)
   437  	}
   438  	for _, f := range files {
   439  		if exitCode := i.runInImage(tag, "test -s "+f); exitCode == 0 {
   440  			t.Errorf("The file %q must be empty", f)
   441  		}
   442  	}
   443  }
   444  
   445  func (i *integrationTest) exerciseIncrementalBuild(tag, imageName string, removePreviousImage bool, expectClean bool, checkOnBuild bool) {
   446  	t := i.t
   447  	start := time.Now()
   448  	config := &api.Config{
   449  		DockerConfig:        docker.GetDefaultDockerConfig(),
   450  		BuilderImage:        imageName,
   451  		BuilderPullPolicy:   api.DefaultBuilderPullPolicy,
   452  		Source:              git.MustParse(TestSource),
   453  		Tag:                 tag,
   454  		Incremental:         false,
   455  		RemovePreviousImage: removePreviousImage,
   456  		ExcludeRegExp:       tar.DefaultExclusionPattern.String(),
   457  	}
   458  
   459  	builder, _, err := strategies.GetStrategy(engineClient, config)
   460  	if err != nil {
   461  		t.Fatalf("Unable to create builder: %v", err)
   462  	}
   463  	resp, err := builder.Build(config)
   464  	if err != nil {
   465  		t.Fatalf("Unexpected error occurred during build: %v", err)
   466  	}
   467  	if !resp.Success {
   468  		t.Fatalf("S2I build failed.")
   469  	}
   470  
   471  	previousImageID := resp.ImageID
   472  	config = &api.Config{
   473  		DockerConfig:            docker.GetDefaultDockerConfig(),
   474  		BuilderImage:            imageName,
   475  		BuilderPullPolicy:       api.DefaultBuilderPullPolicy,
   476  		Source:                  git.MustParse(TestSource),
   477  		Tag:                     tag,
   478  		Incremental:             true,
   479  		RemovePreviousImage:     removePreviousImage,
   480  		PreviousImagePullPolicy: api.PullIfNotPresent,
   481  		ExcludeRegExp:           tar.DefaultExclusionPattern.String(),
   482  	}
   483  
   484  	builder, _, err = strategies.GetStrategy(engineClient, config)
   485  	if err != nil {
   486  		t.Fatalf("Unable to create incremental builder: %v", err)
   487  	}
   488  	resp, err = builder.Build(config)
   489  	if err != nil {
   490  		t.Fatalf("Unexpected error occurred during incremental build: %v", err)
   491  	}
   492  	if !resp.Success {
   493  		t.Fatalf("S2I incremental build failed.")
   494  	}
   495  
   496  	i.checkForImage(tag)
   497  	containerID := i.createContainer(tag)
   498  	defer i.removeContainer(containerID)
   499  	i.checkIncrementalBuildState(containerID, resp.WorkingDir, expectClean)
   500  
   501  	_, err = i.InspectImage(previousImageID)
   502  	if removePreviousImage {
   503  		if err == nil {
   504  			t.Errorf("Previous image %s not deleted", previousImageID)
   505  		}
   506  	} else {
   507  		if err != nil {
   508  			t.Errorf("Couldn't find previous image %s", previousImageID)
   509  		}
   510  	}
   511  
   512  	if checkOnBuild {
   513  		i.fileExists(containerID, "/sti-fake/src/onbuild")
   514  	}
   515  
   516  	if took := time.Since(start); took > docker.DefaultDockerTimeout {
   517  		// https://github.com/openshift/source-to-image/issues/301 is a
   518  		// case where incremental builds would get stuck until the
   519  		// timeout.
   520  		t.Errorf("Test took too long (%v), some operation may have gotten stuck waiting for the DefaultDockerTimeout (%v). Inspect the logs to find operations that took long.", took, docker.DefaultDockerTimeout)
   521  	}
   522  }
   523  
   524  // Support methods
   525  func (i *integrationTest) checkForImage(tag string) {
   526  	_, err := i.InspectImage(tag)
   527  	if err != nil {
   528  		i.t.Errorf("Couldn't find image with tag: %s", tag)
   529  	}
   530  }
   531  
   532  func (i *integrationTest) createContainer(image string) string {
   533  	ctx, cancel := getDefaultContext()
   534  	defer cancel()
   535  	opts := dockertypes.ContainerCreateConfig{Name: "", Config: &dockercontainer.Config{Image: image}}
   536  	container, err := engineClient.ContainerCreate(ctx, opts.Config, opts.HostConfig, opts.NetworkingConfig, opts.Name)
   537  	if err != nil {
   538  		i.t.Errorf("Couldn't create container from image %s with error %+v", image, err)
   539  		return ""
   540  	}
   541  
   542  	ctx, cancel = getDefaultContext()
   543  	defer cancel()
   544  	err = engineClient.ContainerStart(ctx, container.ID, dockertypes.ContainerStartOptions{})
   545  	if err != nil {
   546  		i.t.Errorf("Couldn't start container: %s with error %+v", container.ID, err)
   547  		return ""
   548  	}
   549  
   550  	ctx, cancel = getDefaultContext()
   551  	defer cancel()
   552  	waitC, errC := engineClient.ContainerWait(ctx, container.ID, dockercontainer.WaitConditionNextExit)
   553  	select {
   554  	case result := <-waitC:
   555  		if result.StatusCode != 0 {
   556  			i.t.Errorf("Bad exit code from container: %d", result.StatusCode)
   557  			return ""
   558  		}
   559  	case err := <-errC:
   560  		i.t.Errorf("Error waiting for container: %v", err)
   561  		return ""
   562  	}
   563  	return container.ID
   564  }
   565  
   566  func (i *integrationTest) runInContainer(image string, command []string) int {
   567  	ctx, cancel := getDefaultContext()
   568  	defer cancel()
   569  	opts := dockertypes.ContainerCreateConfig{Name: "", Config: &dockercontainer.Config{Image: image, AttachStdout: false, AttachStdin: false, Cmd: command}}
   570  	container, err := engineClient.ContainerCreate(ctx, opts.Config, opts.HostConfig, opts.NetworkingConfig, opts.Name)
   571  	if err != nil {
   572  		i.t.Errorf("Couldn't create container from image %s err %+v", image, err)
   573  		return -1
   574  	}
   575  
   576  	ctx, cancel = getDefaultContext()
   577  	defer cancel()
   578  	err = engineClient.ContainerStart(ctx, container.ID, dockertypes.ContainerStartOptions{})
   579  	if err != nil {
   580  		i.t.Errorf("Couldn't start container: %s", container.ID)
   581  	}
   582  	ctx, cancel = getDefaultContext()
   583  	defer cancel()
   584  	waitC, errC := engineClient.ContainerWait(ctx, container.ID, dockercontainer.WaitConditionNextExit)
   585  	exitCode := -1
   586  	select {
   587  	case result := <-waitC:
   588  		exitCode = int(result.StatusCode)
   589  	case err := <-errC:
   590  		i.t.Errorf("Couldn't wait for container: %s: %v", container.ID, err)
   591  	}
   592  	ctx, cancel = getDefaultContext()
   593  	defer cancel()
   594  	err = engineClient.ContainerRemove(ctx, container.ID, dockertypes.ContainerRemoveOptions{})
   595  	if err != nil {
   596  		i.t.Errorf("Couldn't remove container: %s", container.ID)
   597  	}
   598  	return exitCode
   599  }
   600  
   601  func (i *integrationTest) removeContainer(cID string) {
   602  	ctx, cancel := getDefaultContext()
   603  	defer cancel()
   604  	engineClient.ContainerKill(ctx, cID, "SIGKILL")
   605  	removeOpts := dockertypes.ContainerRemoveOptions{
   606  		RemoveVolumes: true,
   607  	}
   608  	err := engineClient.ContainerRemove(ctx, cID, removeOpts)
   609  	if err != nil {
   610  		i.t.Errorf("Couldn't remove container %s: %s", cID, err)
   611  	}
   612  }
   613  
   614  func (i *integrationTest) fileExists(cID string, filePath string) {
   615  	res := i.fileExistsInContainer(cID, filePath)
   616  
   617  	if !res {
   618  		i.t.Errorf("Couldn't find file %s in container %s", filePath, cID)
   619  	}
   620  }
   621  
   622  func (i *integrationTest) fileNotExists(cID string, filePath string) {
   623  	res := i.fileExistsInContainer(cID, filePath)
   624  
   625  	if res {
   626  		i.t.Errorf("Unexpected file %s in container %s", filePath, cID)
   627  	}
   628  }
   629  
   630  func (i *integrationTest) runInImage(image string, cmd string) int {
   631  	return i.runInContainer(image, []string{"/bin/sh", "-c", cmd})
   632  }
   633  
   634  func (i *integrationTest) checkBasicBuildState(cID string, workingDir string) {
   635  	i.fileExists(cID, "/sti-fake/assemble-invoked")
   636  	i.fileExists(cID, "/sti-fake/run-invoked")
   637  	i.fileExists(cID, "/sti-fake/src/Gemfile")
   638  
   639  	_, err := os.Stat(workingDir)
   640  	if !os.IsNotExist(err) {
   641  		i.t.Errorf("Unexpected error from stat check on %s", workingDir)
   642  	}
   643  }
   644  
   645  func (i *integrationTest) checkIncrementalBuildState(cID string, workingDir string, expectClean bool) {
   646  	i.checkBasicBuildState(cID, workingDir)
   647  	if expectClean {
   648  		i.fileNotExists(cID, "/sti-fake/save-artifacts-invoked")
   649  	} else {
   650  		i.fileExists(cID, "/sti-fake/save-artifacts-invoked")
   651  	}
   652  }
   653  
   654  func (i *integrationTest) fileExistsInContainer(cID string, filePath string) bool {
   655  	ctx, cancel := getDefaultContext()
   656  	defer cancel()
   657  	rdr, stats, err := engineClient.CopyFromContainer(ctx, cID, filePath)
   658  	if err != nil {
   659  		return false
   660  	}
   661  	defer rdr.Close()
   662  	return "" != stats.Name
   663  }
   664  
   665  func (i *integrationTest) checkForLabel(image string) {
   666  	docker := dockerpkg.New(engineClient, (&api.Config{}).PullAuthentication)
   667  
   668  	labelMap, err := docker.GetLabels(image)
   669  	if err != nil {
   670  		i.t.Fatalf("Unable to get labels from image %s: %v", image, err)
   671  	}
   672  
   673  	if labelMap["testLabel"] != "testLabel_value" {
   674  		i.t.Errorf("Unable to verify 'testLabel' for image '%s'", image)
   675  	}
   676  }