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