github.com/jasei/goreleaser@v0.62.4-0.20180312171904-62cb6a8963a6/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_no_latest": {
    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  				TagTemplates: []string{
   104  					"{{.Version}}",
   105  				},
   106  				Files: []string{
   107  					"testdata/extra_file.txt",
   108  				},
   109  			},
   110  			expect: []string{
   111  				registry + "goreleaser/test_run_pipe:1.0.0",
   112  			},
   113  			assertError: shouldNotErr,
   114  		},
   115  		"valid_dont_publish": {
   116  			publish: false,
   117  			docker: config.Docker{
   118  				Image:      registry + "goreleaser/test_run_pipe",
   119  				Goos:       "linux",
   120  				Goarch:     "amd64",
   121  				Dockerfile: "testdata/Dockerfile",
   122  				Binary:     "mybin",
   123  				TagTemplates: []string{
   124  					"{{.Tag}}-{{.Env.FOO}}",
   125  					"latest",
   126  				},
   127  				Files: []string{
   128  					"testdata/extra_file.txt",
   129  				},
   130  			},
   131  			expect: []string{
   132  				registry + "goreleaser/test_run_pipe:v1.0.0-123",
   133  				registry + "goreleaser/test_run_pipe:latest",
   134  			},
   135  			assertError: shouldNotErr,
   136  		},
   137  		"bad_dockerfile": {
   138  			publish: true,
   139  			docker: config.Docker{
   140  				Image:      registry + "goreleaser/test_run_pipe",
   141  				Goos:       "linux",
   142  				Goarch:     "amd64",
   143  				Dockerfile: "testdata/Dockerfile.bad",
   144  				Binary:     "mybin",
   145  				TagTemplates: []string{
   146  					"{{.Version}}",
   147  				},
   148  			},
   149  			assertError: shouldErr("pull access denied for nope, repository does not exist"),
   150  		},
   151  		"template_error": {
   152  			publish: true,
   153  			docker: config.Docker{
   154  				Image:      registry + "goreleaser/test_run_pipe",
   155  				Goos:       "linux",
   156  				Goarch:     "amd64",
   157  				Dockerfile: "testdata/Dockerfile",
   158  				Binary:     "mybin",
   159  				TagTemplates: []string{
   160  					"{{.Tag}",
   161  				},
   162  			},
   163  			assertError: shouldErr(`template: tag:1: unexpected "}" in operand`),
   164  		},
   165  		"missing_env_on_template": {
   166  			publish: true,
   167  			docker: config.Docker{
   168  				Image:      registry + "goreleaser/test_run_pipe",
   169  				Goos:       "linux",
   170  				Goarch:     "amd64",
   171  				Dockerfile: "testdata/Dockerfile",
   172  				Binary:     "mybin",
   173  				TagTemplates: []string{
   174  					"{{.Env.NOPE}}",
   175  				},
   176  			},
   177  			assertError: shouldErr(`template: tag:1:6: executing "tag" at <.Env.NOPE>: map has no entry for key "NOPE"`),
   178  		},
   179  		"no_permissions": {
   180  			publish: true,
   181  			docker: config.Docker{
   182  				Image:      "docker.io/nope",
   183  				Goos:       "linux",
   184  				Goarch:     "amd64",
   185  				Binary:     "mybin",
   186  				Dockerfile: "testdata/Dockerfile",
   187  				TagTemplates: []string{
   188  					"{{.Tag}}",
   189  					"latest",
   190  				},
   191  				Latest: true,
   192  			},
   193  			expect: []string{
   194  				"docker.io/nope:latest",
   195  				"docker.io/nope:v1.0.0",
   196  			},
   197  			assertError: shouldErr(`requested access to the resource is denied`),
   198  		},
   199  		"dockerfile_doesnt_exist": {
   200  			publish: true,
   201  			docker: config.Docker{
   202  				Image:      "whatever",
   203  				Goos:       "linux",
   204  				Goarch:     "amd64",
   205  				Binary:     "mybin",
   206  				Dockerfile: "testdata/Dockerfilezzz",
   207  				TagTemplates: []string{
   208  					"{{.Tag}}",
   209  				},
   210  			},
   211  			assertError: shouldErr(`failed to link dockerfile`),
   212  		},
   213  		"extra_file_doesnt_exist": {
   214  			publish: true,
   215  			docker: config.Docker{
   216  				Image:  "whatever",
   217  				Goos:   "linux",
   218  				Goarch: "amd64",
   219  				Binary: "mybin",
   220  				Files: []string{
   221  					"testdata/nope.txt",
   222  				},
   223  				Dockerfile: "testdata/Dockerfile",
   224  				TagTemplates: []string{
   225  					"{{.Tag}}",
   226  				},
   227  			},
   228  			assertError: shouldErr(`failed to link extra file 'testdata/nope.txt'`),
   229  		},
   230  		"no_matching_binaries": {
   231  			publish: true,
   232  			docker: config.Docker{
   233  				Image:      "whatever",
   234  				Goos:       "darwin",
   235  				Goarch:     "amd64",
   236  				Binary:     "mybinnnn",
   237  				Dockerfile: "testdata/Dockerfile",
   238  			},
   239  			assertError: shouldNotErr,
   240  		},
   241  	}
   242  
   243  	killAndRm(t)
   244  	start(t)
   245  	defer killAndRm(t)
   246  
   247  	for name, docker := range table {
   248  		t.Run(name, func(tt *testing.T) {
   249  			folder, err := ioutil.TempDir("", "archivetest")
   250  			assert.NoError(tt, err)
   251  			var dist = filepath.Join(folder, "dist")
   252  			assert.NoError(tt, os.Mkdir(dist, 0755))
   253  			assert.NoError(tt, os.Mkdir(filepath.Join(dist, "mybin"), 0755))
   254  			var binPath = filepath.Join(dist, "mybin", "mybin")
   255  			_, err = os.Create(binPath)
   256  			assert.NoError(tt, err)
   257  
   258  			var ctx = context.New(config.Project{
   259  				ProjectName: "mybin",
   260  				Dist:        dist,
   261  				Dockers: []config.Docker{
   262  					docker.docker,
   263  				},
   264  			})
   265  			ctx.SkipPublish = !docker.publish
   266  			ctx.Env = map[string]string{
   267  				"FOO": "123",
   268  			}
   269  			ctx.Version = "1.0.0"
   270  			ctx.Git = context.GitInfo{
   271  				CurrentTag: "v1.0.0",
   272  			}
   273  			for _, os := range []string{"linux", "darwin"} {
   274  				for _, arch := range []string{"amd64", "386"} {
   275  					ctx.Artifacts.Add(artifact.Artifact{
   276  						Name:   "mybin",
   277  						Path:   binPath,
   278  						Goarch: arch,
   279  						Goos:   os,
   280  						Type:   artifact.Binary,
   281  						Extra: map[string]string{
   282  							"Binary": "mybin",
   283  						},
   284  					})
   285  				}
   286  			}
   287  
   288  			// this might fail as the image doesnt exist yet, so lets ignore the error
   289  			for _, img := range docker.expect {
   290  				_ = exec.Command("docker", "rmi", img).Run()
   291  			}
   292  
   293  			docker.assertError(t, Pipe{}.Run(ctx))
   294  
   295  			// this might should not fail as the image should have been created when
   296  			// the step ran
   297  			for _, img := range docker.expect {
   298  				tt.Log("removing docker image", img)
   299  				assert.NoError(tt, exec.Command("docker", "rmi", img).Run(), "could not delete image %s", img)
   300  			}
   301  
   302  		})
   303  	}
   304  }
   305  
   306  func TestDescription(t *testing.T) {
   307  	assert.NotEmpty(t, Pipe{}.String())
   308  }
   309  
   310  func TestNoDockers(t *testing.T) {
   311  	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{}))))
   312  }
   313  
   314  func TestNoDockerWithoutImageName(t *testing.T) {
   315  	assert.True(t, pipeline.IsSkip(Pipe{}.Run(context.New(config.Project{
   316  		Dockers: []config.Docker{
   317  			{
   318  				Goos: "linux",
   319  			},
   320  		},
   321  	}))))
   322  }
   323  
   324  func TestDockerNotInPath(t *testing.T) {
   325  	var path = os.Getenv("PATH")
   326  	defer func() {
   327  		assert.NoError(t, os.Setenv("PATH", path))
   328  	}()
   329  	assert.NoError(t, os.Setenv("PATH", ""))
   330  	var ctx = &context.Context{
   331  		Version: "1.0.0",
   332  		Config: config.Project{
   333  			Dockers: []config.Docker{
   334  				{
   335  					Image: "a/b",
   336  				},
   337  			},
   338  		},
   339  	}
   340  	assert.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
   341  }
   342  
   343  func TestDefault(t *testing.T) {
   344  	var ctx = &context.Context{
   345  		Config: config.Project{
   346  			Builds: []config.Build{
   347  				{
   348  					Binary: "foo",
   349  				},
   350  			},
   351  			Dockers: []config.Docker{
   352  				{
   353  					Latest: true,
   354  				},
   355  			},
   356  		},
   357  	}
   358  	assert.NoError(t, Pipe{}.Default(ctx))
   359  	assert.Len(t, ctx.Config.Dockers, 1)
   360  	var docker = ctx.Config.Dockers[0]
   361  	assert.Equal(t, "linux", docker.Goos)
   362  	assert.Equal(t, "amd64", docker.Goarch)
   363  	assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
   364  	assert.Equal(t, "Dockerfile", docker.Dockerfile)
   365  	assert.Empty(t, docker.OldTagTemplate)
   366  	assert.Equal(t, []string{"{{ .Version }}", "latest"}, docker.TagTemplates)
   367  
   368  }
   369  
   370  func TestDefaultNoDockers(t *testing.T) {
   371  	var ctx = &context.Context{
   372  		Config: config.Project{
   373  			Dockers: []config.Docker{},
   374  		},
   375  	}
   376  	assert.NoError(t, Pipe{}.Default(ctx))
   377  	assert.Empty(t, ctx.Config.Dockers)
   378  }
   379  
   380  func TestDefaultSet(t *testing.T) {
   381  	var ctx = &context.Context{
   382  		Config: config.Project{
   383  			Dockers: []config.Docker{
   384  				{
   385  					Goos:       "windows",
   386  					Goarch:     "i386",
   387  					Binary:     "bar",
   388  					Dockerfile: "Dockerfile.foo",
   389  				},
   390  			},
   391  		},
   392  	}
   393  	assert.NoError(t, Pipe{}.Default(ctx))
   394  	assert.Len(t, ctx.Config.Dockers, 1)
   395  	var docker = ctx.Config.Dockers[0]
   396  	assert.Equal(t, "windows", docker.Goos)
   397  	assert.Equal(t, "i386", docker.Goarch)
   398  	assert.Equal(t, "bar", docker.Binary)
   399  	assert.Empty(t, docker.OldTagTemplate)
   400  	assert.Equal(t, []string{"{{ .Version }}"}, docker.TagTemplates)
   401  	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
   402  }
   403  
   404  func TestDefaultWithOldTagTemplateSet(t *testing.T) {
   405  	var ctx = &context.Context{
   406  		Config: config.Project{
   407  			Dockers: []config.Docker{
   408  				{
   409  					Dockerfile:     "Dockerfile.foo",
   410  					OldTagTemplate: "{{.Tag}}",
   411  					Latest:         true,
   412  					Binary:         "foo",
   413  				},
   414  			},
   415  		},
   416  	}
   417  	assert.NoError(t, Pipe{}.Default(ctx))
   418  	assert.Len(t, ctx.Config.Dockers, 1)
   419  	var docker = ctx.Config.Dockers[0]
   420  	assert.Equal(t, []string{"{{.Tag}}", "latest"}, docker.TagTemplates)
   421  	assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
   422  }
   423  
   424  func TestLinkFile(t *testing.T) {
   425  	const srcFile = "/tmp/test"
   426  	const dstFile = "/tmp/linked"
   427  	err := ioutil.WriteFile(srcFile, []byte("foo"), 0644)
   428  	if err != nil {
   429  		t.Log("Cannot setup test file")
   430  		t.Fail()
   431  	}
   432  	err = link(srcFile, dstFile)
   433  	if err != nil {
   434  		t.Log("Failed to link: ", err)
   435  		t.Fail()
   436  	}
   437  	if inode(srcFile) != inode(dstFile) {
   438  		t.Log("Inodes do not match, destination file is not a link")
   439  		t.Fail()
   440  	}
   441  	// cleanup
   442  	os.Remove(srcFile)
   443  	os.Remove(dstFile)
   444  }
   445  
   446  func TestLinkDirectory(t *testing.T) {
   447  	const srcDir = "/tmp/testdir"
   448  	const testFile = "test"
   449  	const dstDir = "/tmp/linkedDir"
   450  
   451  	os.Mkdir(srcDir, 0755)
   452  	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
   453  	if err != nil {
   454  		t.Log("Cannot setup test file")
   455  		t.Fail()
   456  	}
   457  	err = link(srcDir, dstDir)
   458  	if err != nil {
   459  		t.Log("Failed to link: ", err)
   460  		t.Fail()
   461  	}
   462  	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
   463  		t.Log("Inodes do not match, destination file is not a link")
   464  		t.Fail()
   465  	}
   466  
   467  	// cleanup
   468  	os.RemoveAll(srcDir)
   469  	os.RemoveAll(dstDir)
   470  }
   471  
   472  func TestLinkTwoLevelDirectory(t *testing.T) {
   473  	const srcDir = "/tmp/testdir"
   474  	const srcLevel2 = srcDir + "/level2"
   475  	const testFile = "test"
   476  	const dstDir = "/tmp/linkedDir"
   477  
   478  	os.Mkdir(srcDir, 0755)
   479  	os.Mkdir(srcLevel2, 0755)
   480  	err := ioutil.WriteFile(srcDir+"/"+testFile, []byte("foo"), 0644)
   481  	if err != nil {
   482  		t.Log("Cannot setup test file")
   483  		t.Fail()
   484  	}
   485  	err = ioutil.WriteFile(srcLevel2+"/"+testFile, []byte("foo"), 0644)
   486  	if err != nil {
   487  		t.Log("Cannot setup test file")
   488  		t.Fail()
   489  	}
   490  	err = link(srcDir, dstDir)
   491  	if err != nil {
   492  		t.Log("Failed to link: ", err)
   493  		t.Fail()
   494  	}
   495  	if inode(srcDir+"/"+testFile) != inode(dstDir+"/"+testFile) {
   496  		t.Log("Inodes do not match")
   497  		t.Fail()
   498  	}
   499  	if inode(srcLevel2+"/"+testFile) != inode(dstDir+"/level2/"+testFile) {
   500  		t.Log("Inodes do not match")
   501  		t.Fail()
   502  	}
   503  	// cleanup
   504  	os.RemoveAll(srcDir)
   505  	os.RemoveAll(dstDir)
   506  }
   507  
   508  func inode(file string) uint64 {
   509  	fileInfo, err := os.Stat(file)
   510  	if err != nil {
   511  		return 0
   512  	}
   513  	stat := fileInfo.Sys().(*syscall.Stat_t)
   514  	return stat.Ino
   515  }