github.com/droot/goreleaser@v0.66.2-0.20180420030140-c2db5fb17157/pipeline/docker/docker_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"flag"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"syscall"
    10  	"testing"
    11  
    12  	"github.com/goreleaser/goreleaser/config"
    13  	"github.com/goreleaser/goreleaser/context"
    14  	"github.com/goreleaser/goreleaser/internal/artifact"
    15  	"github.com/goreleaser/goreleaser/pipeline"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  var it = flag.Bool("it", false, "push images to docker hub")
    20  var registry = "localhost:5000/"
    21  
    22  func TestMain(m *testing.M) {
    23  	flag.Parse()
    24  	if *it {
    25  		registry = "docker.io/"
    26  	}
    27  	os.Exit(m.Run())
    28  }
    29  
    30  func start(t *testing.T) {
    31  	if *it {
    32  		return
    33  	}
    34  	if out, err := exec.Command(
    35  		"docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2",
    36  	).CombinedOutput(); err != nil {
    37  		t.Log("failed to start docker registry", string(out), err)
    38  		t.FailNow()
    39  	}
    40  }
    41  
    42  func killAndRm(t *testing.T) {
    43  	if *it {
    44  		return
    45  	}
    46  	t.Log("killing registry")
    47  	_ = exec.Command("docker", "kill", "registry").Run()
    48  	_ = exec.Command("docker", "rm", "registry").Run()
    49  }
    50  
    51  func TestRunPipe(t *testing.T) {
    52  	type errChecker func(*testing.T, error)
    53  	var shouldErr = func(msg string) errChecker {
    54  		return func(t *testing.T, err error) {
    55  			assert.Error(t, err)
    56  			assert.Contains(t, err.Error(), msg)
    57  		}
    58  	}
    59  	var shouldNotErr = func(t *testing.T, err error) {
    60  		assert.NoError(t, err)
    61  	}
    62  
    63  	var table = map[string]struct {
    64  		docker      config.Docker
    65  		publish     bool
    66  		expect      []string
    67  		assertError errChecker
    68  	}{
    69  		"valid": {
    70  			publish: true,
    71  			docker: config.Docker{
    72  				Image:      registry + "goreleaser/test_run_pipe",
    73  				Goos:       "linux",
    74  				Goarch:     "amd64",
    75  				Dockerfile: "testdata/Dockerfile",
    76  				Binary:     "mybin",
    77  				TagTemplates: []string{
    78  					"{{.Tag}}-{{.Env.FOO}}",
    79  					"v{{.Major}}",
    80  					"v{{.Major}}.{{.Minor}}",
    81  					"latest",
    82  				},
    83  				Files: []string{
    84  					"testdata/extra_file.txt",
    85  				},
    86  			},
    87  			expect: []string{
    88  				registry + "goreleaser/test_run_pipe:v1.0.0-123",
    89  				registry + "goreleaser/test_run_pipe:v1",
    90  				registry + "goreleaser/test_run_pipe:v1.0",
    91  				registry + "goreleaser/test_run_pipe:latest",
    92  			},
    93  			assertError: shouldNotErr,
    94  		},
    95  		"valid_skip_push": {
    96  			publish: true,
    97  			docker: config.Docker{
    98  				Image:      registry + "goreleaser/test_run_pipe",
    99  				Goos:       "linux",
   100  				Goarch:     "amd64",
   101  				Dockerfile: "testdata/Dockerfile",
   102  				Binary:     "mybin",
   103  				SkipPush:   true,
   104  				TagTemplates: []string{
   105  					"{{.Tag}}-{{.Env.FOO}}",
   106  					"v{{.Major}}",
   107  					"v{{.Major}}.{{.Minor}}",
   108  					"latest",
   109  				},
   110  				Files: []string{
   111  					"testdata/extra_file.txt",
   112  				},
   113  			},
   114  			expect: []string{
   115  				registry + "goreleaser/test_run_pipe:v1.0.0-123",
   116  				registry + "goreleaser/test_run_pipe:v1",
   117  				registry + "goreleaser/test_run_pipe:v1.0",
   118  				registry + "goreleaser/test_run_pipe:latest",
   119  			},
   120  			assertError: shouldNotErr,
   121  		},
   122  		"valid_no_latest": {
   123  			publish: true,
   124  			docker: config.Docker{
   125  				Image:      registry + "goreleaser/test_run_pipe",
   126  				Goos:       "linux",
   127  				Goarch:     "amd64",
   128  				Dockerfile: "testdata/Dockerfile",
   129  				Binary:     "mybin",
   130  				TagTemplates: []string{
   131  					"{{.Version}}",
   132  				},
   133  				Files: []string{
   134  					"testdata/extra_file.txt",
   135  				},
   136  			},
   137  			expect: []string{
   138  				registry + "goreleaser/test_run_pipe:1.0.0",
   139  			},
   140  			assertError: shouldNotErr,
   141  		},
   142  		"valid_dont_publish": {
   143  			publish: false,
   144  			docker: config.Docker{
   145  				Image:      registry + "goreleaser/test_run_pipe",
   146  				Goos:       "linux",
   147  				Goarch:     "amd64",
   148  				Dockerfile: "testdata/Dockerfile",
   149  				Binary:     "mybin",
   150  				TagTemplates: []string{
   151  					"{{.Tag}}-{{.Env.FOO}}",
   152  					"latest",
   153  				},
   154  				Files: []string{
   155  					"testdata/extra_file.txt",
   156  				},
   157  			},
   158  			expect: []string{
   159  				registry + "goreleaser/test_run_pipe:v1.0.0-123",
   160  				registry + "goreleaser/test_run_pipe:latest",
   161  			},
   162  			assertError: shouldNotErr,
   163  		},
   164  		"bad_dockerfile": {
   165  			publish: true,
   166  			docker: config.Docker{
   167  				Image:      registry + "goreleaser/test_run_pipe",
   168  				Goos:       "linux",
   169  				Goarch:     "amd64",
   170  				Dockerfile: "testdata/Dockerfile.bad",
   171  				Binary:     "mybin",
   172  				TagTemplates: []string{
   173  					"{{.Version}}",
   174  				},
   175  			},
   176  			assertError: shouldErr("pull access denied for nope, repository does not exist"),
   177  		},
   178  		"template_error": {
   179  			publish: true,
   180  			docker: config.Docker{
   181  				Image:      registry + "goreleaser/test_run_pipe",
   182  				Goos:       "linux",
   183  				Goarch:     "amd64",
   184  				Dockerfile: "testdata/Dockerfile",
   185  				Binary:     "mybin",
   186  				TagTemplates: []string{
   187  					"{{.Tag}",
   188  				},
   189  			},
   190  			assertError: shouldErr(`template: tag:1: unexpected "}" in operand`),
   191  		},
   192  		"missing_env_on_template": {
   193  			publish: true,
   194  			docker: config.Docker{
   195  				Image:      registry + "goreleaser/test_run_pipe",
   196  				Goos:       "linux",
   197  				Goarch:     "amd64",
   198  				Dockerfile: "testdata/Dockerfile",
   199  				Binary:     "mybin",
   200  				TagTemplates: []string{
   201  					"{{.Env.NOPE}}",
   202  				},
   203  			},
   204  			assertError: shouldErr(`template: tag:1:6: executing "tag" at <.Env.NOPE>: map has no entry for key "NOPE"`),
   205  		},
   206  		"no_permissions": {
   207  			publish: true,
   208  			docker: config.Docker{
   209  				Image:      "docker.io/nope",
   210  				Goos:       "linux",
   211  				Goarch:     "amd64",
   212  				Binary:     "mybin",
   213  				Dockerfile: "testdata/Dockerfile",
   214  				TagTemplates: []string{
   215  					"{{.Tag}}",
   216  					"latest",
   217  				},
   218  				Latest: true,
   219  			},
   220  			expect: []string{
   221  				"docker.io/nope:latest",
   222  				"docker.io/nope:v1.0.0",
   223  			},
   224  			assertError: shouldErr(`requested access to the resource is denied`),
   225  		},
   226  		"dockerfile_doesnt_exist": {
   227  			publish: true,
   228  			docker: config.Docker{
   229  				Image:      "whatever",
   230  				Goos:       "linux",
   231  				Goarch:     "amd64",
   232  				Binary:     "mybin",
   233  				Dockerfile: "testdata/Dockerfilezzz",
   234  				TagTemplates: []string{
   235  					"{{.Tag}}",
   236  				},
   237  			},
   238  			assertError: shouldErr(`failed to link dockerfile`),
   239  		},
   240  		"extra_file_doesnt_exist": {
   241  			publish: true,
   242  			docker: config.Docker{
   243  				Image:  "whatever",
   244  				Goos:   "linux",
   245  				Goarch: "amd64",
   246  				Binary: "mybin",
   247  				Files: []string{
   248  					"testdata/nope.txt",
   249  				},
   250  				Dockerfile: "testdata/Dockerfile",
   251  				TagTemplates: []string{
   252  					"{{.Tag}}",
   253  				},
   254  			},
   255  			assertError: shouldErr(`failed to link extra file 'testdata/nope.txt'`),
   256  		},
   257  		"no_matching_binaries": {
   258  			publish: true,
   259  			docker: config.Docker{
   260  				Image:      "whatever",
   261  				Goos:       "darwin",
   262  				Goarch:     "amd64",
   263  				Binary:     "mybinnnn",
   264  				Dockerfile: "testdata/Dockerfile",
   265  			},
   266  			assertError: shouldNotErr,
   267  		},
   268  	}
   269  
   270  	killAndRm(t)
   271  	start(t)
   272  	defer killAndRm(t)
   273  
   274  	for name, docker := range table {
   275  		t.Run(name, func(tt *testing.T) {
   276  			folder, err := ioutil.TempDir("", "archivetest")
   277  			assert.NoError(tt, err)
   278  			var dist = filepath.Join(folder, "dist")
   279  			assert.NoError(tt, os.Mkdir(dist, 0755))
   280  			assert.NoError(tt, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
   281  			var binPath = filepath.Join(dist, "mybin", "mybin")
   282  			_, err = os.Create(binPath)
   283  			assert.NoError(tt, err)
   284  
   285  			var ctx = context.New(config.Project{
   286  				ProjectName: "mybin",
   287  				Dist:        dist,
   288  				Dockers: []config.Docker{
   289  					docker.docker,
   290  				},
   291  			})
   292  			ctx.SkipPublish = !docker.publish
   293  			ctx.Env = map[string]string{
   294  				"FOO": "123",
   295  			}
   296  			ctx.Version = "1.0.0"
   297  			ctx.Git = context.GitInfo{
   298  				CurrentTag: "v1.0.0",
   299  			}
   300  			for _, os := range []string{"linux", "darwin"} {
   301  				for _, arch := range []string{"amd64", "386"} {
   302  					ctx.Artifacts.Add(artifact.Artifact{
   303  						Name:   "mybin",
   304  						Path:   binPath,
   305  						Goarch: arch,
   306  						Goos:   os,
   307  						Type:   artifact.Binary,
   308  						Extra: map[string]string{
   309  							"Binary": "mybin",
   310  						},
   311  					})
   312  				}
   313  			}
   314  
   315  			// this might fail as the image doesnt exist yet, so lets ignore the error
   316  			for _, img := range docker.expect {
   317  				_ = exec.Command("docker", "rmi", img).Run()
   318  			}
   319  
   320  			docker.assertError(t, Pipe{}.Run(ctx))
   321  
   322  			// this might should not fail as the image should have been created when
   323  			// the step ran
   324  			for _, img := range docker.expect {
   325  				tt.Log("removing docker image", img)
   326  				assert.NoError(tt, exec.Command("docker", "rmi", img).Run(), "could not delete image %s", img)
   327  			}
   328  
   329  		})
   330  	}
   331  }
   332  
   333  func TestDescription(t *testing.T) {
   334  	assert.NotEmpty(t, Pipe{}.String())
   335  }
   336  
   337  func TestNoDockers(t *testing.T) {
   338  	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{}))))
   339  }
   340  
   341  func TestNoDockerWithoutImageName(t *testing.T) {
   342  	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{
   343  		Dockers: []config.Docker{
   344  			{
   345  				Goos: "linux",
   346  			},
   347  		},
   348  	}))))
   349  }
   350  
   351  func TestDockerNotInPath(t *testing.T) {
   352  	var path = os.Getenv("PATH")
   353  	defer func() {
   354  		assert.NoError(t, os.Setenv("PATH", path))
   355  	}()
   356  	assert.NoError(t, os.Setenv("PATH", ""))
   357  	var ctx = &context.Context{
   358  		Version: "1.0.0",
   359  		Config: config.Project{
   360  			Dockers: []config.Docker{
   361  				{
   362  					Image: "a/b",
   363  				},
   364  			},
   365  		},
   366  	}
   367  	assert.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
   368  }
   369  
   370  func TestDefault(t *testing.T) {
   371  	var ctx = &context.Context{
   372  		Config: config.Project{
   373  			Builds: []config.Build{
   374  				{
   375  					Binary: "foo",
   376  				},
   377  			},
   378  			Dockers: []config.Docker{
   379  				{
   380  					Latest: true,
   381  				},
   382  			},
   383  		},
   384  	}
   385  	assert.NoError(t, Pipe{}.Default(ctx))
   386  	assert.Len(t, ctx.Config.Dockers, 1)
   387  	var docker = ctx.Config.Dockers[0]
   388  	assert.Equal(t, "linux", docker.Goos)
   389  	assert.Equal(t, "amd64", docker.Goarch)
   390  	assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
   391  	assert.Equal(t, "Dockerfile", docker.Dockerfile)
   392  	assert.Empty(t, docker.OldTagTemplate)
   393  	assert.Equal(t, []string{"{{ .Version }}", "latest"}, docker.TagTemplates)
   394  
   395  }
   396  
   397  func TestDefaultNoDockers(t *testing.T) {
   398  	var ctx = &context.Context{
   399  		Config: config.Project{
   400  			Dockers: []config.Docker{},
   401  		},
   402  	}
   403  	assert.NoError(t, Pipe{}.Default(ctx))
   404  	assert.Empty(t, ctx.Config.Dockers)
   405  }
   406  
   407  func TestDefaultSet(t *testing.T) {
   408  	var ctx = &context.Context{
   409  		Config: config.Project{
   410  			Dockers: []config.Docker{
   411  				{
   412  					Goos:       "windows",
   413  					Goarch:     "i386",
   414  					Binary:     "bar",
   415  					Dockerfile: "Dockerfile.foo",
   416  				},
   417  			},
   418  		},
   419  	}
   420  	assert.NoError(t, Pipe{}.Default(ctx))
   421  	assert.Len(t, ctx.Config.Dockers, 1)
   422  	var docker = ctx.Config.Dockers[0]
   423  	assert.Equal(t, "windows", docker.Goos)
   424  	assert.Equal(t, "i386", docker.Goarch)
   425  	assert.Equal(t, "bar", docker.Binary)
   426  	assert.Empty(t, docker.OldTagTemplate)
   427  	assert.Equal(t, []string{"{{ .Version }}"}, docker.TagTemplates)
   428  	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
   429  }
   430  
   431  func TestDefaultWithOldTagTemplateSet(t *testing.T) {
   432  	var ctx = &context.Context{
   433  		Config: config.Project{
   434  			Dockers: []config.Docker{
   435  				{
   436  					Dockerfile:     "Dockerfile.foo",
   437  					OldTagTemplate: "{{.Tag}}",
   438  					Latest:         true,
   439  					Binary:         "foo",
   440  				},
   441  			},
   442  		},
   443  	}
   444  	assert.NoError(t, Pipe{}.Default(ctx))
   445  	assert.Len(t, ctx.Config.Dockers, 1)
   446  	var docker = ctx.Config.Dockers[0]
   447  	assert.Equal(t, []string{"{{.Tag}}", "latest"}, docker.TagTemplates)
   448  	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
   449  }
   450  
   451  func TestLinkFile(t *testing.T) {
   452  	const srcFile = "/tmp/test"
   453  	const dstFile = "/tmp/linked"
   454  	err := ioutil.WriteFile(srcFile, []byte("foo"), 0644)
   455  	if err != nil {
   456  		t.Log("Cannot setup test file")
   457  		t.Fail()
   458  	}
   459  	err = link(srcFile, dstFile)
   460  	if err != nil {
   461  		t.Log("Failed to link: ", err)
   462  		t.Fail()
   463  	}
   464  	if inode(srcFile) != inode(dstFile) {
   465  		t.Log("Inodes do not match, destination file is not a link")
   466  		t.Fail()
   467  	}
   468  	// cleanup
   469  	os.Remove(srcFile)
   470  	os.Remove(dstFile)
   471  }
   472  
   473  func TestLinkDirectory(t *testing.T) {
   474  	const srcDir = "/tmp/testdir"
   475  	const testFile = "test"
   476  	const dstDir = "/tmp/linkedDir"
   477  
   478  	os.Mkdir(srcDir, 0755)
   479  	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
   480  	if err != nil {
   481  		t.Log("Cannot setup test file")
   482  		t.Fail()
   483  	}
   484  	err = link(srcDir, dstDir)
   485  	if err != nil {
   486  		t.Log("Failed to link: ", err)
   487  		t.Fail()
   488  	}
   489  	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
   490  		t.Log("Inodes do not match, destination file is not a link")
   491  		t.Fail()
   492  	}
   493  
   494  	// cleanup
   495  	os.RemoveAll(srcDir)
   496  	os.RemoveAll(dstDir)
   497  }
   498  
   499  func TestLinkTwoLevelDirectory(t *testing.T) {
   500  	const srcDir = "/tmp/testdir"
   501  	const srcLevel2 = srcDir + "/level2"
   502  	const testFile = "test"
   503  	const dstDir = "/tmp/linkedDir"
   504  
   505  	os.Mkdir(srcDir, 0755)
   506  	os.Mkdir(srcLevel2, 0755)
   507  	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
   508  	if err != nil {
   509  		t.Log("Cannot setup test file")
   510  		t.Fail()
   511  	}
   512  	err = ioutil.WriteFile(srcLevel2+"/"+testFile, []byte("foo"), 0644)
   513  	if err != nil {
   514  		t.Log("Cannot setup test file")
   515  		t.Fail()
   516  	}
   517  	err = link(srcDir, dstDir)
   518  	if err != nil {
   519  		t.Log("Failed to link: ", err)
   520  		t.Fail()
   521  	}
   522  	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
   523  		t.Log("Inodes do not match")
   524  		t.Fail()
   525  	}
   526  	if inode(srcLevel2+"/"+testFile) != inode(dstDir+"/level2/"+testFile) {
   527  		t.Log("Inodes do not match")
   528  		t.Fail()
   529  	}
   530  	// cleanup
   531  	os.RemoveAll(srcDir)
   532  	os.RemoveAll(dstDir)
   533  }
   534  
   535  func inode(file string) uint64 {
   536  	fileInfo, err := os.Stat(file)
   537  	if err != nil {
   538  		return 0
   539  	}
   540  	stat := fileInfo.Sys().(*syscall.Stat_t)
   541  	return stat.Ino
   542  }