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 }