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 }