github.com/grahambrereton-form3/tilt@v0.10.18/internal/build/container_test.go (about)

     1  //+build !skipcontainertests
     2  
     3  // Tests that involve spinning up/interacting with actual containers
     4  package build
     5  
     6  import (
     7  	"io/ioutil"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/docker/distribution/reference"
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	"github.com/windmilleng/tilt/internal/docker"
    16  	"github.com/windmilleng/tilt/internal/dockerfile"
    17  	"github.com/windmilleng/tilt/pkg/model"
    18  )
    19  
    20  // * * * IMAGE BUILDER * * *
    21  
    22  func TestDockerBuildDockerfile(t *testing.T) {
    23  	f := newDockerBuildFixture(t)
    24  	defer f.teardown()
    25  
    26  	df := dockerfile.Dockerfile(`
    27  FROM alpine
    28  WORKDIR /src
    29  ADD a.txt .
    30  RUN cp a.txt b.txt
    31  ADD dir/c.txt .
    32  `)
    33  
    34  	f.WriteFile("a.txt", "a")
    35  	f.WriteFile("dir/c.txt", "c")
    36  	f.WriteFile("missing.txt", "missing")
    37  
    38  	ref, err := f.b.BuildImage(f.ctx, f.ps, f.getNameFromTest(), df, f.Path(), model.EmptyMatcher, model.DockerBuildArgs{}, "")
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	f.assertImageHasLabels(ref, docker.BuiltByTiltLabel)
    44  
    45  	pcs := []expectedFile{
    46  		expectedFile{Path: "/src/a.txt", Contents: "a"},
    47  		expectedFile{Path: "/src/b.txt", Contents: "a"},
    48  		expectedFile{Path: "/src/c.txt", Contents: "c"},
    49  		expectedFile{Path: "/src/dir/c.txt", Missing: true},
    50  		expectedFile{Path: "/src/missing.txt", Missing: true},
    51  	}
    52  	f.assertFilesInImage(ref, pcs)
    53  }
    54  
    55  func TestDockerBuildWithBuildArgs(t *testing.T) {
    56  	f := newDockerBuildFixture(t)
    57  	defer f.teardown()
    58  
    59  	df := dockerfile.Dockerfile(`FROM alpine
    60  ARG some_variable_name
    61  
    62  ADD $some_variable_name /test.txt`)
    63  
    64  	f.WriteFile("awesome_variable", "hi im an awesome variable")
    65  
    66  	ba := model.DockerBuildArgs{
    67  		"some_variable_name": "awesome_variable",
    68  	}
    69  	ref, err := f.b.BuildImage(f.ctx, f.ps, f.getNameFromTest(), df, f.Path(), model.EmptyMatcher, ba, "")
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  
    74  	expected := []expectedFile{
    75  		expectedFile{Path: "/test.txt", Contents: "hi im an awesome variable"},
    76  	}
    77  	f.assertFilesInImage(ref, expected)
    78  }
    79  
    80  func TestSync(t *testing.T) {
    81  	f := newDockerBuildFixture(t)
    82  	defer f.teardown()
    83  
    84  	// write some files in to it
    85  	f.WriteFile("hi/hello", "hi hello")
    86  	f.WriteFile("sup", "my name is dan")
    87  
    88  	s := model.Sync{
    89  		LocalPath:     f.Path(),
    90  		ContainerPath: "/src",
    91  	}
    92  
    93  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	pcs := []expectedFile{
    99  		expectedFile{Path: "/src/hi/hello", Contents: "hi hello"},
   100  		expectedFile{Path: "/src/sup", Contents: "my name is dan"},
   101  	}
   102  	f.assertFilesInImage(ref, pcs)
   103  }
   104  
   105  func TestSyncFileToDirectory(t *testing.T) {
   106  	f := newDockerBuildFixture(t)
   107  	defer f.teardown()
   108  
   109  	f.WriteFile("sup", "my name is dan")
   110  
   111  	s := model.Sync{
   112  		LocalPath:     f.JoinPath("sup"),
   113  		ContainerPath: "/src/",
   114  	}
   115  
   116  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	pcs := []expectedFile{
   122  		expectedFile{Path: "/src/sup", Contents: "my name is dan"},
   123  	}
   124  	f.assertFilesInImage(ref, pcs)
   125  }
   126  
   127  func TestMultipleSyncs(t *testing.T) {
   128  	f := newDockerBuildFixture(t)
   129  	defer f.teardown()
   130  
   131  	// write some files in to it
   132  	f.WriteFile("hi/hello", "hi hello")
   133  	f.WriteFile("bye/ciao/goodbye", "bye laterz")
   134  
   135  	s1 := model.Sync{
   136  		LocalPath:     f.JoinPath("hi"),
   137  		ContainerPath: "/hello_there",
   138  	}
   139  	s2 := model.Sync{
   140  		LocalPath:     f.JoinPath("bye"),
   141  		ContainerPath: "goodbye_there",
   142  	}
   143  
   144  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{s1, s2}, model.EmptyMatcher, nil, model.Cmd{})
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  
   149  	pcs := []expectedFile{
   150  		expectedFile{Path: "/hello_there/hello", Contents: "hi hello"},
   151  		expectedFile{Path: "/goodbye_there/ciao/goodbye", Contents: "bye laterz"},
   152  	}
   153  	f.assertFilesInImage(ref, pcs)
   154  }
   155  
   156  func TestSyncCollisions(t *testing.T) {
   157  	f := newDockerBuildFixture(t)
   158  	defer f.teardown()
   159  
   160  	// write some files in to it
   161  	f.WriteFile("hi/hello", "hi hello")
   162  	f.WriteFile("bye/hello", "bye laterz")
   163  
   164  	// Sync-ing two files to the same place in the container -- expect the second file
   165  	// to take precedence (file should contain "bye laterz")
   166  	s1 := model.Sync{
   167  		LocalPath:     f.JoinPath("hi"),
   168  		ContainerPath: "/hello_there",
   169  	}
   170  	s2 := model.Sync{
   171  		LocalPath:     f.JoinPath("bye"),
   172  		ContainerPath: "/hello_there",
   173  	}
   174  
   175  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{s1, s2}, model.EmptyMatcher, nil, model.Cmd{})
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	pcs := []expectedFile{
   181  		expectedFile{Path: "/hello_there/hello", Contents: "bye laterz"},
   182  	}
   183  	f.assertFilesInImage(ref, pcs)
   184  }
   185  
   186  func TestPush(t *testing.T) {
   187  	f := newDockerBuildFixture(t)
   188  	defer f.teardown()
   189  
   190  	f.startRegistry()
   191  
   192  	// write some files in to it
   193  	f.WriteFile("hi/hello", "hi hello")
   194  	f.WriteFile("sup", "my name is dan")
   195  
   196  	s := model.Sync{
   197  		LocalPath:     f.Path(),
   198  		ContainerPath: "/src",
   199  	}
   200  
   201  	name, err := reference.WithName("localhost:5005/myimage")
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  
   206  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, name, simpleDockerfile, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  
   211  	namedTagged, err := f.b.PushImage(f.ctx, ref, ioutil.Discard)
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	pcs := []expectedFile{
   217  		expectedFile{Path: "/src/hi/hello", Contents: "hi hello"},
   218  		expectedFile{Path: "/src/sup", Contents: "my name is dan"},
   219  	}
   220  
   221  	f.assertFilesInImage(namedTagged, pcs)
   222  }
   223  
   224  func TestPushInvalid(t *testing.T) {
   225  	f := newDockerBuildFixture(t)
   226  	defer f.teardown()
   227  
   228  	s := model.Sync{
   229  		LocalPath:     f.Path(),
   230  		ContainerPath: "/src",
   231  	}
   232  	name, err := reference.WithName("localhost:5005/myimage")
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, name, simpleDockerfile, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
   237  	if err != nil {
   238  		t.Fatal(err)
   239  	}
   240  
   241  	_, err = f.b.PushImage(f.ctx, ref, ioutil.Discard)
   242  	msg := `pushing image "localhost:5005/myimage"`
   243  	if err == nil || !strings.Contains(err.Error(), msg) {
   244  		t.Fatalf("Expected error containing %q, actual: %v", msg, err)
   245  	}
   246  }
   247  
   248  func TestBuildOneRun(t *testing.T) {
   249  	f := newDockerBuildFixture(t)
   250  	defer f.teardown()
   251  
   252  	runs := model.ToRuns([]model.Cmd{
   253  		model.ToShellCmd("echo -n hello >> hi"),
   254  	})
   255  
   256  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{}, model.EmptyMatcher, runs, model.Cmd{})
   257  	if err != nil {
   258  		t.Fatal(err)
   259  	}
   260  
   261  	expected := []expectedFile{
   262  		expectedFile{Path: "hi", Contents: "hello"},
   263  	}
   264  	f.assertFilesInImage(ref, expected)
   265  }
   266  
   267  func TestBuildMultipleRuns(t *testing.T) {
   268  	f := newDockerBuildFixture(t)
   269  	defer f.teardown()
   270  
   271  	runs := model.ToRuns([]model.Cmd{
   272  		model.ToShellCmd("echo -n hello >> hi"),
   273  		model.ToShellCmd("echo -n sup >> hi2"),
   274  	})
   275  
   276  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{}, model.EmptyMatcher, runs, model.Cmd{})
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	expected := []expectedFile{
   282  		expectedFile{Path: "hi", Contents: "hello"},
   283  		expectedFile{Path: "hi2", Contents: "sup"},
   284  	}
   285  	f.assertFilesInImage(ref, expected)
   286  }
   287  
   288  func TestBuildMultipleRunsRemoveFiles(t *testing.T) {
   289  	f := newDockerBuildFixture(t)
   290  	defer f.teardown()
   291  
   292  	runs := model.ToRuns([]model.Cmd{
   293  		model.Cmd{Argv: []string{"sh", "-c", "echo -n hello >> hi"}},
   294  		model.Cmd{Argv: []string{"sh", "-c", "echo -n sup >> hi2"}},
   295  		model.Cmd{Argv: []string{"sh", "-c", "rm hi"}},
   296  	})
   297  
   298  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{}, model.EmptyMatcher, runs, model.Cmd{})
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  
   303  	expected := []expectedFile{
   304  		expectedFile{Path: "hi2", Contents: "sup"},
   305  		expectedFile{Path: "hi", Missing: true},
   306  	}
   307  	f.assertFilesInImage(ref, expected)
   308  }
   309  
   310  func TestBuildFailingRun(t *testing.T) {
   311  	f := newDockerBuildFixture(t)
   312  	defer f.teardown()
   313  
   314  	runs := model.ToRuns([]model.Cmd{
   315  		model.ToShellCmd("echo hello && exit 1"),
   316  	})
   317  
   318  	_, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{}, model.EmptyMatcher, runs, model.Cmd{})
   319  	if assert.NotNil(t, err) {
   320  		assert.Contains(t, err.Error(), "hello")
   321  
   322  		// Different versions of docker have a different error string
   323  		hasExitCode1 := strings.Contains(err.Error(), "exit code 1") ||
   324  			strings.Contains(err.Error(), "returned a non-zero code: 1") ||
   325  			strings.Contains(err.Error(), "exit code: 1")
   326  		if !hasExitCode1 {
   327  			t.Errorf("Expected failure with exit code 1, actual: %v", err)
   328  		}
   329  	}
   330  }
   331  
   332  func TestEntrypoint(t *testing.T) {
   333  	f := newDockerBuildFixture(t)
   334  	defer f.teardown()
   335  
   336  	entrypoint := model.ToShellCmd("echo -n hello >> hi")
   337  	d, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, nil, model.EmptyMatcher, nil, entrypoint)
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  
   342  	expected := []expectedFile{
   343  		expectedFile{Path: "hi", Contents: "hello"},
   344  	}
   345  
   346  	// Start container WITHOUT overriding entrypoint (which assertFilesInImage... does)
   347  	cID := f.startContainer(f.ctx, containerConfig(d))
   348  	f.assertFilesInContainer(f.ctx, cID, expected)
   349  }
   350  
   351  func TestDockerfileWithEntrypointPermitted(t *testing.T) {
   352  	f := newDockerBuildFixture(t)
   353  	defer f.teardown()
   354  
   355  	df := dockerfile.Dockerfile(`FROM alpine
   356  ENTRYPOINT ["sleep", "100000"]`)
   357  
   358  	_, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), df, nil, model.EmptyMatcher, nil, model.Cmd{})
   359  	if err != nil {
   360  		t.Errorf("Unexpected error %v", err)
   361  	}
   362  }
   363  
   364  func TestReapOneImage(t *testing.T) {
   365  	f := newDockerBuildFixture(t)
   366  	defer f.teardown()
   367  
   368  	s := model.Sync{
   369  		LocalPath:     f.Path(),
   370  		ContainerPath: "/src",
   371  	}
   372  
   373  	df1 := simpleDockerfile
   374  	ref1, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), df1, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
   375  	if err != nil {
   376  		t.Fatal(err)
   377  	}
   378  
   379  	label := dockerfile.Label("tilt.reaperTest")
   380  	f.b.extraLabels[label] = "1"
   381  	df2 := simpleDockerfile.Run(model.ToShellCmd("echo hi >> hi.txt"))
   382  	ref2, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), df2, []model.Sync{s}, model.EmptyMatcher, nil, model.Cmd{})
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  
   387  	err = f.reaper.RemoveTiltImages(f.ctx, time.Now().Add(time.Second), false, FilterByLabel(label))
   388  	if err != nil {
   389  		t.Fatal(err)
   390  	}
   391  
   392  	f.assertImageExists(ref1)
   393  	f.assertImageNotExists(ref2)
   394  }
   395  
   396  func TestConditionalRunInRealDocker(t *testing.T) {
   397  	f := newDockerBuildFixture(t)
   398  	defer f.teardown()
   399  
   400  	f.WriteFile("a.txt", "a")
   401  	f.WriteFile("b.txt", "b")
   402  
   403  	s := model.Sync{
   404  		LocalPath:     f.Path(),
   405  		ContainerPath: "/src",
   406  	}
   407  	run1 := model.Run{
   408  		Cmd:      model.ToShellCmd("cat /src/a.txt >> /src/c.txt"),
   409  		Triggers: model.NewPathSet([]string{"a.txt"}, f.Path()),
   410  	}
   411  	run2 := model.Run{
   412  		Cmd: model.ToShellCmd("cat /src/b.txt >> /src/d.txt"),
   413  	}
   414  
   415  	ref, err := f.b.DeprecatedFastBuildImage(f.ctx, f.ps, f.getNameFromTest(), simpleDockerfile, []model.Sync{s}, model.EmptyMatcher, []model.Run{run1, run2}, model.Cmd{})
   416  	if err != nil {
   417  		t.Fatal(err)
   418  	}
   419  
   420  	pcs := []expectedFile{
   421  		expectedFile{Path: "/src/a.txt", Contents: "a"},
   422  		expectedFile{Path: "/src/b.txt", Contents: "b"},
   423  		expectedFile{Path: "/src/c.txt", Contents: "a"},
   424  		expectedFile{Path: "/src/d.txt", Contents: "b"},
   425  	}
   426  	f.assertFilesInImage(ref, pcs)
   427  }