github.com/nak3/source-to-image@v1.1.10-0.20180319140719-2ed55639898d/pkg/build/strategies/sti/sti_test.go (about) 1 package sti 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "path/filepath" 8 "reflect" 9 "regexp/syntax" 10 "strings" 11 "testing" 12 13 "github.com/openshift/source-to-image/pkg/api" 14 "github.com/openshift/source-to-image/pkg/build" 15 "github.com/openshift/source-to-image/pkg/docker" 16 s2ierr "github.com/openshift/source-to-image/pkg/errors" 17 "github.com/openshift/source-to-image/pkg/ignore" 18 "github.com/openshift/source-to-image/pkg/scm/downloaders/empty" 19 "github.com/openshift/source-to-image/pkg/scm/downloaders/file" 20 gitdownloader "github.com/openshift/source-to-image/pkg/scm/downloaders/git" 21 "github.com/openshift/source-to-image/pkg/scm/git" 22 "github.com/openshift/source-to-image/pkg/test" 23 testfs "github.com/openshift/source-to-image/pkg/test/fs" 24 "github.com/openshift/source-to-image/pkg/util/fs" 25 ) 26 27 type FakeSTI struct { 28 CleanupCalled bool 29 PrepareCalled bool 30 SetupRequired []string 31 SetupOptional []string 32 SetupError error 33 ExistsCalled bool 34 ExistsError error 35 BuildRequest *api.Config 36 BuildResult *api.Result 37 DownloadError error 38 SaveArtifactsCalled bool 39 SaveArtifactsError error 40 FetchSourceCalled bool 41 FetchSourceError error 42 ExecuteCommand string 43 ExecuteUser string 44 ExecuteError error 45 ExpectedError bool 46 LayeredBuildCalled bool 47 LayeredBuildError error 48 PostExecuteDestination string 49 PostExecuteContainerID string 50 PostExecuteError error 51 } 52 53 func newFakeBaseSTI() *STI { 54 return &STI{ 55 config: &api.Config{}, 56 result: &api.Result{}, 57 docker: &docker.FakeDocker{}, 58 installer: &test.FakeInstaller{}, 59 git: &test.FakeGit{}, 60 fs: &testfs.FakeFileSystem{}, 61 tar: &test.FakeTar{}, 62 } 63 } 64 65 func newFakeSTI(f *FakeSTI) *STI { 66 s := &STI{ 67 config: &api.Config{}, 68 result: &api.Result{}, 69 docker: &docker.FakeDocker{}, 70 runtimeDocker: &docker.FakeDocker{}, 71 installer: &test.FakeInstaller{}, 72 git: &test.FakeGit{}, 73 fs: &testfs.FakeFileSystem{}, 74 tar: &test.FakeTar{}, 75 preparer: f, 76 ignorer: &ignore.DockerIgnorer{}, 77 artifacts: f, 78 scripts: f, 79 garbage: f, 80 layered: &FakeDockerBuild{f}, 81 } 82 s.source = &gitdownloader.Clone{Git: s.git, FileSystem: s.fs} 83 return s 84 } 85 86 func (f *FakeSTI) Cleanup(*api.Config) { 87 f.CleanupCalled = true 88 } 89 90 func (f *FakeSTI) Prepare(*api.Config) error { 91 f.PrepareCalled = true 92 f.SetupRequired = []string{api.Assemble, api.Run} 93 f.SetupOptional = []string{api.SaveArtifacts} 94 return nil 95 } 96 97 func (f *FakeSTI) Exists(*api.Config) bool { 98 f.ExistsCalled = true 99 return true 100 } 101 102 func (f *FakeSTI) Request() *api.Config { 103 return f.BuildRequest 104 } 105 106 func (f *FakeSTI) Result() *api.Result { 107 return f.BuildResult 108 } 109 110 func (f *FakeSTI) Save(*api.Config) error { 111 f.SaveArtifactsCalled = true 112 return f.SaveArtifactsError 113 } 114 115 func (f *FakeSTI) fetchSource() error { 116 return f.FetchSourceError 117 } 118 119 func (f *FakeSTI) Download(*api.Config) (*git.SourceInfo, error) { 120 return nil, f.DownloadError 121 } 122 123 func (f *FakeSTI) Execute(command string, user string, r *api.Config) error { 124 f.ExecuteCommand = command 125 f.ExecuteUser = user 126 return f.ExecuteError 127 } 128 129 func (f *FakeSTI) wasExpectedError(text string) bool { 130 return f.ExpectedError 131 } 132 133 func (f *FakeSTI) PostExecute(id, destination string) error { 134 f.PostExecuteContainerID = id 135 f.PostExecuteDestination = destination 136 return f.PostExecuteError 137 } 138 139 type FakeDockerBuild struct { 140 *FakeSTI 141 } 142 143 func (f *FakeDockerBuild) Build(*api.Config) (*api.Result, error) { 144 f.LayeredBuildCalled = true 145 return &api.Result{}, f.LayeredBuildError 146 } 147 148 func TestDefaultSource(t *testing.T) { 149 config := &api.Config{ 150 Source: git.MustParse("."), 151 DockerConfig: &api.DockerConfig{Endpoint: "unix:///var/run/docker.sock"}, 152 } 153 client, err := docker.NewEngineAPIClient(config.DockerConfig) 154 if err != nil { 155 t.Fatal(err) 156 } 157 sti, err := New(client, config, fs.NewFileSystem(), build.Overrides{}) 158 if err != nil { 159 t.Fatal(err) 160 } 161 if config.Source == nil { 162 t.Errorf("Config.Source not set: %v", config.Source) 163 } 164 if _, ok := sti.source.(*file.File); !ok || sti.source == nil { 165 t.Errorf("Source interface not set: %#v", sti.source) 166 } 167 } 168 169 func TestEmptySource(t *testing.T) { 170 config := &api.Config{ 171 Source: nil, 172 DockerConfig: &api.DockerConfig{Endpoint: "unix:///var/run/docker.sock"}, 173 } 174 client, err := docker.NewEngineAPIClient(config.DockerConfig) 175 if err != nil { 176 t.Fatal(err) 177 } 178 sti, err := New(client, config, fs.NewFileSystem(), build.Overrides{}) 179 if err != nil { 180 t.Fatal(err) 181 } 182 if config.Source != nil { 183 t.Errorf("Config.Source unexpectantly changed: %v", config.Source) 184 } 185 if _, ok := sti.source.(*empty.Noop); !ok || sti.source == nil { 186 t.Errorf("Source interface not set: %#v", sti.source) 187 } 188 } 189 190 func TestOverrides(t *testing.T) { 191 fd := &FakeSTI{} 192 client, err := docker.NewEngineAPIClient(&api.DockerConfig{Endpoint: "unix:///var/run/docker.sock"}) 193 if err != nil { 194 t.Fatal(err) 195 } 196 sti, err := New(client, 197 &api.Config{ 198 DockerConfig: &api.DockerConfig{Endpoint: "unix:///var/run/docker.sock"}, 199 }, 200 fs.NewFileSystem(), 201 build.Overrides{ 202 Downloader: fd, 203 }, 204 ) 205 if err != nil { 206 t.Fatal(err) 207 } 208 if sti.source != fd { 209 t.Errorf("Override of downloader not set: %#v", sti) 210 } 211 } 212 213 func TestBuild(t *testing.T) { 214 incrementalTest := []bool{false, true} 215 for _, incremental := range incrementalTest { 216 fh := &FakeSTI{ 217 BuildRequest: &api.Config{Incremental: incremental}, 218 BuildResult: &api.Result{}, 219 } 220 221 builder := newFakeSTI(fh) 222 builder.Build(&api.Config{Incremental: incremental}) 223 224 // Verify the right scripts were configed 225 if !reflect.DeepEqual(fh.SetupRequired, []string{api.Assemble, api.Run}) { 226 t.Errorf("Unexpected required scripts configed: %#v", fh.SetupRequired) 227 } 228 if !reflect.DeepEqual(fh.SetupOptional, []string{api.SaveArtifacts}) { 229 t.Errorf("Unexpected optional scripts configed: %#v", fh.SetupOptional) 230 } 231 232 // Verify that Exists was called 233 if !fh.ExistsCalled { 234 t.Errorf("Exists was not called.") 235 } 236 237 // Verify that Save was called for an incremental build 238 if incremental && !fh.SaveArtifactsCalled { 239 t.Errorf("Save artifacts was not called for an incremental build") 240 } 241 242 // Verify that Execute was called with the right script 243 if fh.ExecuteCommand != api.Assemble { 244 t.Errorf("Unexpected execute command: %s", fh.ExecuteCommand) 245 } 246 } 247 } 248 249 func TestLayeredBuild(t *testing.T) { 250 fh := &FakeSTI{ 251 BuildRequest: &api.Config{ 252 BuilderImage: "testimage", 253 }, 254 BuildResult: &api.Result{ 255 BuildInfo: api.BuildInfo{ 256 Stages: []api.StageInfo{}, 257 }, 258 }, 259 ExecuteError: errMissingRequirements, 260 ExpectedError: true, 261 } 262 builder := newFakeSTI(fh) 263 builder.Build(&api.Config{BuilderImage: "testimage"}) 264 // Verify layered build 265 if !fh.LayeredBuildCalled { 266 t.Errorf("Layered build was not called.") 267 } 268 } 269 270 func TestBuildErrorExecute(t *testing.T) { 271 fh := &FakeSTI{ 272 BuildRequest: &api.Config{ 273 BuilderImage: "testimage", 274 }, 275 BuildResult: &api.Result{}, 276 ExecuteError: errors.New("ExecuteError"), 277 ExpectedError: false, 278 } 279 builder := newFakeSTI(fh) 280 _, err := builder.Build(&api.Config{BuilderImage: "testimage"}) 281 if err == nil || err.Error() != "ExecuteError" { 282 t.Errorf("An error was expected, but got different %v", err) 283 } 284 } 285 286 func TestWasExpectedError(t *testing.T) { 287 type expErr struct { 288 text string 289 expected bool 290 } 291 292 tests := []expErr{ 293 { // 0 - tar error 294 text: `/bin/sh: tar: not found`, 295 expected: true, 296 }, 297 { // 1 - tar error 298 text: `/bin/sh: tar: command not found`, 299 expected: true, 300 }, 301 { // 2 - /bin/sh error 302 text: `exec: "/bin/sh": stat /bin/sh: no such file or directory`, 303 expected: true, 304 }, 305 { // 3 - non container error 306 text: "other error", 307 expected: false, 308 }, 309 } 310 311 for i, ti := range tests { 312 result := isMissingRequirements(ti.text) 313 if result != ti.expected { 314 t.Errorf("(%d) Unexpected result: %v. Expected: %v", i, result, ti.expected) 315 } 316 } 317 } 318 319 func testBuildHandler() *STI { 320 s := &STI{ 321 docker: &docker.FakeDocker{}, 322 incrementalDocker: &docker.FakeDocker{}, 323 installer: &test.FakeInstaller{}, 324 git: &test.FakeGit{}, 325 fs: &testfs.FakeFileSystem{ExistsResult: map[string]bool{filepath.FromSlash("a-repo-source"): true}}, 326 tar: &test.FakeTar{}, 327 config: &api.Config{}, 328 result: &api.Result{}, 329 callbackInvoker: &test.FakeCallbackInvoker{}, 330 } 331 s.source = &gitdownloader.Clone{Git: s.git, FileSystem: s.fs} 332 s.initPostExecutorSteps() 333 return s 334 } 335 336 func TestPostExecute(t *testing.T) { 337 type postExecuteTest struct { 338 tag string 339 incremental bool 340 previousImageID string 341 scriptsFromImage bool 342 } 343 testCases := []postExecuteTest{ 344 // 0: tagged, incremental, without previous image 345 {"test/tag", true, "", true}, 346 // 1: tagged, incremental, with previous image 347 {"test/tag", true, "test-image", true}, 348 // 2: tagged, no incremental, without previous image 349 {"test/tag", false, "", true}, 350 // 3: tagged, no incremental, with previous image 351 {"test/tag", false, "test-image", true}, 352 353 // 4: no tag, incremental, without previous image 354 {"", true, "", false}, 355 // 5: no tag, incremental, with previous image 356 {"", true, "test-image", false}, 357 // 6: no tag, no incremental, without previous image 358 {"", false, "", false}, 359 // 7: no tag, no incremental, with previous image 360 {"", false, "test-image", false}, 361 } 362 363 for i, tc := range testCases { 364 bh := testBuildHandler() 365 containerID := "test-container-id" 366 bh.result.Messages = []string{"one", "two"} 367 bh.config.Tag = tc.tag 368 bh.config.Incremental = tc.incremental 369 dh := bh.docker.(*docker.FakeDocker) 370 if tc.previousImageID != "" { 371 bh.config.RemovePreviousImage = true 372 bh.incremental = tc.incremental 373 bh.docker.(*docker.FakeDocker).GetImageIDResult = tc.previousImageID 374 } 375 if tc.scriptsFromImage { 376 bh.scriptsURL = map[string]string{api.Run: "image:///usr/libexec/s2i/run"} 377 } 378 err := bh.PostExecute(containerID, "cmd1") 379 if err != nil { 380 t.Errorf("(%d) Unexpected error from postExecute: %v", i, err) 381 } 382 // Ensure CommitContainer was called with the right parameters 383 expectedCmd := []string{"cmd1/scripts/" + api.Run} 384 if tc.scriptsFromImage { 385 expectedCmd = []string{"/usr/libexec/s2i/" + api.Run} 386 } 387 if !reflect.DeepEqual(dh.CommitContainerOpts.Command, expectedCmd) { 388 t.Errorf("(%d) Unexpected commit container command: %#v, expected %q", i, dh.CommitContainerOpts.Command, expectedCmd) 389 } 390 if dh.CommitContainerOpts.Repository != tc.tag { 391 t.Errorf("(%d) Unexpected tag committed, expected %q, got %q", i, tc.tag, dh.CommitContainerOpts.Repository) 392 } 393 // Ensure image removal when incremental and previousImageID present 394 if tc.incremental && tc.previousImageID != "" { 395 if dh.RemoveImageName != "test-image" { 396 t.Errorf("(%d) Previous image was not removed: %q", i, dh.RemoveImageName) 397 } 398 } else { 399 if dh.RemoveImageName != "" { 400 t.Errorf("(%d) Unexpected image removed: %s", i, dh.RemoveImageName) 401 } 402 } 403 } 404 } 405 406 func TestExists(t *testing.T) { 407 type incrementalTest struct { 408 // incremental flag was passed 409 incremental bool 410 // previous image existence 411 previousImage bool 412 // script installed 413 scriptInstalled bool 414 // expected result 415 expected bool 416 } 417 418 tests := []incrementalTest{ 419 // 0-1: incremental, no image, no matter what with scripts 420 {true, false, false, false}, 421 {true, false, true, false}, 422 423 // 2: incremental, previous image, no scripts 424 {true, true, false, false}, 425 // 3: incremental, previous image, scripts installed 426 {true, true, true, true}, 427 428 // 4-7: no incremental build - should always return false no matter what other flags are 429 {false, false, false, false}, 430 {false, false, true, false}, 431 {false, true, false, false}, 432 {false, true, true, false}, 433 } 434 435 for i, ti := range tests { 436 bh := testBuildHandler() 437 bh.config.WorkingDir = "/working-dir" 438 bh.config.Incremental = ti.incremental 439 bh.config.BuilderPullPolicy = api.PullAlways 440 bh.installedScripts = map[string]bool{api.SaveArtifacts: ti.scriptInstalled} 441 bh.incrementalDocker.(*docker.FakeDocker).PullResult = ti.previousImage 442 bh.config.DockerConfig = &api.DockerConfig{Endpoint: "http://localhost:4243"} 443 incremental := bh.Exists(bh.config) 444 if incremental != ti.expected { 445 t.Errorf("(%d) Unexpected incremental result: %v. Expected: %v", 446 i, incremental, ti.expected) 447 } 448 if ti.incremental && ti.previousImage && ti.scriptInstalled { 449 if len(bh.fs.(*testfs.FakeFileSystem).ExistsFile) == 0 { 450 continue 451 } 452 scriptChecked := bh.fs.(*testfs.FakeFileSystem).ExistsFile[0] 453 expectedScript := "/working-dir/upload/scripts/save-artifacts" 454 if scriptChecked != expectedScript { 455 t.Errorf("(%d) Unexpected script checked. Actual: %s. Expected: %s", 456 i, scriptChecked, expectedScript) 457 } 458 } 459 } 460 } 461 462 func TestSaveArtifacts(t *testing.T) { 463 bh := testBuildHandler() 464 bh.config.WorkingDir = "/working-dir" 465 bh.config.Tag = "image/tag" 466 fakeFS := bh.fs.(*testfs.FakeFileSystem) 467 fd := bh.docker.(*docker.FakeDocker) 468 th := bh.tar.(*test.FakeTar) 469 err := bh.Save(bh.config) 470 if err != nil { 471 t.Errorf("Unexpected error when saving artifacts: %v", err) 472 } 473 expectedArtifactDir := "/working-dir/upload/artifacts" 474 if filepath.ToSlash(fakeFS.MkdirDir) != expectedArtifactDir { 475 t.Errorf("Mkdir was not called with the expected directory: %s", 476 fakeFS.MkdirDir) 477 } 478 if fd.RunContainerOpts.Image != bh.config.Tag { 479 t.Errorf("Unexpected image sent to RunContainer: %s", 480 fd.RunContainerOpts.Image) 481 } 482 if filepath.ToSlash(th.ExtractTarDir) != expectedArtifactDir || th.ExtractTarReader == nil { 483 t.Errorf("ExtractTar was not called with the expected parameters.") 484 } 485 } 486 487 func TestSaveArtifactsCustomTag(t *testing.T) { 488 bh := testBuildHandler() 489 bh.config.WorkingDir = "/working-dir" 490 bh.config.IncrementalFromTag = "custom/tag" 491 bh.config.Tag = "image/tag" 492 fakeFS := bh.fs.(*testfs.FakeFileSystem) 493 fd := bh.docker.(*docker.FakeDocker) 494 th := bh.tar.(*test.FakeTar) 495 err := bh.Save(bh.config) 496 if err != nil { 497 t.Errorf("Unexpected error when saving artifacts: %v", err) 498 } 499 expectedArtifactDir := "/working-dir/upload/artifacts" 500 if filepath.ToSlash(fakeFS.MkdirDir) != expectedArtifactDir { 501 t.Errorf("Mkdir was not called with the expected directory: %s", 502 fakeFS.MkdirDir) 503 } 504 if fd.RunContainerOpts.Image != bh.config.IncrementalFromTag { 505 t.Errorf("Unexpected image sent to RunContainer: %s", 506 fd.RunContainerOpts.Image) 507 } 508 if filepath.ToSlash(th.ExtractTarDir) != expectedArtifactDir || th.ExtractTarReader == nil { 509 t.Errorf("ExtractTar was not called with the expected parameters.") 510 } 511 } 512 513 func TestSaveArtifactsRunError(t *testing.T) { 514 tests := []error{ 515 fmt.Errorf("Run error"), 516 s2ierr.NewContainerError("", -1, ""), 517 } 518 expected := []error{ 519 tests[0], 520 s2ierr.NewSaveArtifactsError("", "", tests[1]), 521 } 522 // test with tar extract error or not 523 tarError := []bool{true, false} 524 for i := range tests { 525 for _, te := range tarError { 526 bh := testBuildHandler() 527 fd := bh.docker.(*docker.FakeDocker) 528 th := bh.tar.(*test.FakeTar) 529 fd.RunContainerError = tests[i] 530 if te { 531 th.ExtractTarError = fmt.Errorf("tar error") 532 } 533 err := bh.Save(bh.config) 534 if !te && err != expected[i] { 535 t.Errorf("Unexpected error returned from saveArtifacts: %v", err) 536 } else if te && err != th.ExtractTarError { 537 t.Errorf("Expected tar error. Got %v", err) 538 } 539 } 540 } 541 } 542 543 func TestSaveArtifactsErrorBeforeStart(t *testing.T) { 544 bh := testBuildHandler() 545 fd := bh.docker.(*docker.FakeDocker) 546 expected := fmt.Errorf("run error") 547 fd.RunContainerError = expected 548 fd.RunContainerErrorBeforeStart = true 549 err := bh.Save(bh.config) 550 if err != expected { 551 t.Errorf("Unexpected error returned from saveArtifacts: %v", err) 552 } 553 } 554 555 func TestSaveArtifactsExtractError(t *testing.T) { 556 bh := testBuildHandler() 557 th := bh.tar.(*test.FakeTar) 558 expected := fmt.Errorf("extract error") 559 th.ExtractTarError = expected 560 err := bh.Save(bh.config) 561 if err != expected { 562 t.Errorf("Unexpected error returned from saveArtifacts: %v", err) 563 } 564 } 565 566 func TestFetchSource(t *testing.T) { 567 type fetchTest struct { 568 refSpecified bool 569 checkoutExpected bool 570 } 571 572 tests := []fetchTest{ 573 // 0 574 { 575 refSpecified: false, 576 checkoutExpected: false, 577 }, 578 // 1 579 { 580 refSpecified: true, 581 checkoutExpected: true, 582 }, 583 } 584 585 for testNum, ft := range tests { 586 bh := testBuildHandler() 587 gh := bh.git.(*test.FakeGit) 588 589 bh.config.WorkingDir = "/working-dir" 590 bh.config.Source = git.MustParse("a-repo-source") 591 if ft.refSpecified { 592 bh.config.Source.URL.Fragment = "a-branch" 593 } 594 595 expectedTargetDir := "/working-dir/upload/src" 596 _, e := bh.source.Download(bh.config) 597 if e != nil { 598 t.Errorf("Unexpected error %v [%d]", e, testNum) 599 } 600 if gh.CloneSource.StringNoFragment() != "a-repo-source" { 601 t.Errorf("Clone was not called with the expected source. Got %s, expected %s [%d]", gh.CloneSource, "a-source-repo-source", testNum) 602 } 603 if filepath.ToSlash(gh.CloneTarget) != expectedTargetDir { 604 t.Errorf("Unexpected target directory for clone operation. Got %s, expected %s [%d]", gh.CloneTarget, expectedTargetDir, testNum) 605 } 606 if ft.checkoutExpected { 607 if gh.CheckoutRef != "a-branch" { 608 t.Errorf("Checkout was not called with the expected branch. Got %s, expected %s [%d]", gh.CheckoutRef, "a-branch", testNum) 609 } 610 if filepath.ToSlash(gh.CheckoutRepo) != expectedTargetDir { 611 t.Errorf("Unexpected target repository for checkout operation. Got %s, expected %s [%d]", gh.CheckoutRepo, expectedTargetDir, testNum) 612 } 613 } 614 } 615 } 616 617 func TestPrepareOK(t *testing.T) { 618 rh := newFakeSTI(&FakeSTI{}) 619 rh.SetScripts([]string{api.Assemble, api.Run}, []string{api.SaveArtifacts}) 620 rh.fs.(*testfs.FakeFileSystem).WorkingDirResult = "/working-dir" 621 err := rh.Prepare(rh.config) 622 if err != nil { 623 t.Errorf("An error occurred setting up the config handler: %v", err) 624 } 625 if !rh.fs.(*testfs.FakeFileSystem).WorkingDirCalled { 626 t.Errorf("Working directory was not created.") 627 } 628 var expected []string 629 for _, dir := range workingDirs { 630 expected = append(expected, filepath.FromSlash("/working-dir/"+dir)) 631 } 632 mkdirs := rh.fs.(*testfs.FakeFileSystem).MkdirAllDir 633 if !reflect.DeepEqual(mkdirs, expected) { 634 t.Errorf("Unexpected set of MkdirAll calls: %#v", mkdirs) 635 } 636 scripts := rh.installer.(*test.FakeInstaller).Scripts 637 if !reflect.DeepEqual(scripts[0], []string{api.Assemble, api.Run}) { 638 t.Errorf("Unexpected set of required scripts: %#v", scripts[0]) 639 } 640 if !reflect.DeepEqual(scripts[1], []string{api.SaveArtifacts}) { 641 t.Errorf("Unexpected set of optional scripts: %#v", scripts[1]) 642 } 643 } 644 645 func TestPrepareErrorCreatingWorkingDir(t *testing.T) { 646 rh := newFakeSTI(&FakeSTI{}) 647 rh.fs.(*testfs.FakeFileSystem).WorkingDirError = errors.New("WorkingDirError") 648 err := rh.Prepare(rh.config) 649 if err == nil || err.Error() != "WorkingDirError" { 650 t.Errorf("An error was expected for WorkingDir, but got different: %v", err) 651 } 652 } 653 654 func TestPrepareErrorMkdirAll(t *testing.T) { 655 rh := newFakeSTI(&FakeSTI{}) 656 rh.fs.(*testfs.FakeFileSystem).MkdirAllError = errors.New("MkdirAllError") 657 err := rh.Prepare(rh.config) 658 if err == nil || err.Error() != "MkdirAllError" { 659 t.Errorf("An error was expected for MkdirAll, but got different: %v", err) 660 } 661 } 662 663 func TestPrepareErrorRequiredDownloadAndInstall(t *testing.T) { 664 rh := newFakeSTI(&FakeSTI{}) 665 rh.SetScripts([]string{api.Assemble, api.Run}, []string{api.SaveArtifacts}) 666 rh.installer.(*test.FakeInstaller).Error = fmt.Errorf("%v", api.Assemble) 667 err := rh.Prepare(rh.config) 668 if err == nil || err.Error() != api.Assemble { 669 t.Errorf("An error was expected for required DownloadAndInstall, but got different: %v", err) 670 } 671 } 672 673 func TestPrepareErrorOptionalDownloadAndInstall(t *testing.T) { 674 rh := newFakeSTI(&FakeSTI{}) 675 rh.SetScripts([]string{api.Assemble, api.Run}, []string{api.SaveArtifacts}) 676 err := rh.Prepare(rh.config) 677 if err != nil { 678 t.Errorf("Unexpected error when downloading optional scripts: %v", err) 679 } 680 } 681 682 func TestPrepareUseCustomRuntimeArtifacts(t *testing.T) { 683 expectedMapping := filepath.FromSlash("/src") + ":dst" 684 685 builder := newFakeSTI(&FakeSTI{}) 686 687 config := builder.config 688 config.RuntimeImage = "my-app" 689 config.RuntimeArtifacts.Set(expectedMapping) 690 691 if err := builder.Prepare(config); err != nil { 692 t.Fatalf("Prepare() unexpectedly failed with error: %v", err) 693 } 694 695 if actualMapping := config.RuntimeArtifacts.String(); actualMapping != expectedMapping { 696 t.Errorf("Prepare() shouldn't change mapping, but it was modified from %v to %v", expectedMapping, actualMapping) 697 } 698 } 699 700 func TestPrepareFailForEmptyRuntimeArtifacts(t *testing.T) { 701 builder := newFakeSTI(&FakeSTI{}) 702 703 fakeDocker := builder.docker.(*docker.FakeDocker) 704 fakeDocker.AssembleInputFilesResult = "" 705 706 config := builder.config 707 config.RuntimeImage = "my-app" 708 709 if len(config.RuntimeArtifacts) > 0 { 710 t.Fatalf("RuntimeArtifacts must be empty by default") 711 } 712 713 err := builder.Prepare(config) 714 if err == nil { 715 t.Errorf("Prepare() should fail but it didn't") 716 717 } else if expectedError := "no runtime artifacts to copy"; !strings.Contains(err.Error(), expectedError) { 718 t.Errorf("Prepare() should fail with error that contains text %q but failed with error: %q", expectedError, err) 719 } 720 } 721 722 func TestPrepareRuntimeArtifactsValidation(t *testing.T) { 723 testCases := []struct { 724 mapping string 725 expectedError string 726 }{ 727 { 728 mapping: "src:dst", 729 expectedError: "source must be an absolute path", 730 }, 731 { 732 mapping: "/src:/dst", 733 expectedError: "destination must be a relative path", 734 }, 735 { 736 mapping: "/src:../dst", 737 expectedError: "destination cannot start with '..'", 738 }, 739 } 740 741 for _, testCase := range testCases { 742 for _, mappingFromUser := range []bool{true, false} { 743 builder := newFakeSTI(&FakeSTI{}) 744 745 config := builder.config 746 config.RuntimeImage = "my-app" 747 748 if mappingFromUser { 749 config.RuntimeArtifacts.Set(testCase.mapping) 750 } else { 751 fakeDocker := builder.docker.(*docker.FakeDocker) 752 fakeDocker.AssembleInputFilesResult = testCase.mapping 753 } 754 755 err := builder.Prepare(config) 756 if err == nil { 757 t.Errorf("Prepare() should fail but it didn't") 758 759 } else if !strings.Contains(err.Error(), testCase.expectedError) { 760 t.Errorf("Prepare() should fail to validate mapping %q with error that contains text %q but failed with error: %q", testCase.mapping, testCase.expectedError, err) 761 } 762 } 763 } 764 } 765 766 func TestPrepareSetRuntimeArtifacts(t *testing.T) { 767 for _, mapping := range []string{filepath.FromSlash("/src") + ":dst", filepath.FromSlash("/src1") + ":dst1;" + filepath.FromSlash("/src1") + ":dst1"} { 768 expectedMapping := strings.Replace(mapping, ";", ",", -1) 769 770 builder := newFakeSTI(&FakeSTI{}) 771 772 fakeDocker := builder.docker.(*docker.FakeDocker) 773 fakeDocker.AssembleInputFilesResult = mapping 774 775 config := builder.config 776 config.RuntimeImage = "my-app" 777 778 if len(config.RuntimeArtifacts) > 0 { 779 t.Fatalf("RuntimeArtifacts must be empty by default") 780 } 781 782 if err := builder.Prepare(config); err != nil { 783 t.Fatalf("Prepare() unexpectedly failed with error: %v", err) 784 } 785 786 if actualMapping := config.RuntimeArtifacts.String(); actualMapping != expectedMapping { 787 t.Errorf("Prepare() shouldn't change mapping, but it was modified from %v to %v", expectedMapping, actualMapping) 788 } 789 } 790 } 791 792 func TestPrepareDownloadAssembleRuntime(t *testing.T) { 793 installer := &test.FakeInstaller{} 794 795 builder := newFakeSTI(&FakeSTI{}) 796 builder.runtimeInstaller = installer 797 builder.optionalRuntimeScripts = []string{api.AssembleRuntime} 798 799 config := builder.config 800 config.RuntimeImage = "my-app" 801 config.RuntimeArtifacts.Set("/src:dst") 802 803 if err := builder.Prepare(config); err != nil { 804 t.Fatalf("Prepare() unexpectedly failed with error: %v", err) 805 } 806 807 if len(installer.Scripts) != 1 || installer.Scripts[0][0] != api.AssembleRuntime { 808 t.Errorf("Prepare() should download %q script but it downloaded %v", api.AssembleRuntime, installer.Scripts) 809 } 810 } 811 812 func TestExecuteOK(t *testing.T) { 813 rh := newFakeBaseSTI() 814 pe := &FakeSTI{} 815 rh.postExecutor = pe 816 rh.config.WorkingDir = "/working-dir" 817 rh.config.BuilderImage = "test/image" 818 rh.config.BuilderPullPolicy = api.PullAlways 819 rh.config.Environment = api.EnvironmentList{ 820 api.EnvironmentSpec{ 821 Name: "Key1", 822 Value: "Value1", 823 }, 824 api.EnvironmentSpec{ 825 Name: "Key2", 826 Value: "Value2", 827 }, 828 } 829 expectedEnv := []string{"Key1=Value1", "Key2=Value2"} 830 th := rh.tar.(*test.FakeTar) 831 th.CreateTarResult = "/working-dir/test.tar" 832 fd := rh.docker.(*docker.FakeDocker) 833 fd.RunContainerContainerID = "1234" 834 fd.RunContainerCmd = []string{"one", "two"} 835 836 err := rh.Execute("test-command", "foo", rh.config) 837 if err != nil { 838 t.Errorf("Unexpected error returned: %v", err) 839 } 840 th = rh.tar.(*test.FakeTar).Copy() 841 if th.CreateTarBase != "" { 842 t.Errorf("Unexpected tar base directory: %s", th.CreateTarBase) 843 } 844 if filepath.ToSlash(th.CreateTarDir) != "/working-dir/upload" { 845 t.Errorf("Unexpected tar directory: %s", th.CreateTarDir) 846 } 847 fh, ok := rh.fs.(*testfs.FakeFileSystem) 848 if !ok { 849 t.Fatalf("Unable to convert %v to FakeFilesystem", rh.fs) 850 } 851 if fh.OpenFile != "" { 852 t.Fatalf("Unexpected file opened: %s", fh.OpenFile) 853 } 854 if fh.OpenFileResult != nil { 855 t.Errorf("Tar file was opened.") 856 } 857 ro := fd.RunContainerOpts 858 859 if ro.User != "foo" { 860 t.Errorf("Expected user to be foo, got %q", ro.User) 861 } 862 863 if ro.Image != rh.config.BuilderImage { 864 t.Errorf("Unexpected Image passed to RunContainer") 865 } 866 if _, ok := ro.Stdin.(*io.PipeReader); !ok { 867 t.Errorf("Unexpected input stream: %#v", ro.Stdin) 868 } 869 if ro.PullImage { 870 t.Errorf("PullImage is true for RunContainer, should be false") 871 } 872 if ro.Command != "test-command" { 873 t.Errorf("Unexpected command passed to RunContainer: %s", 874 ro.Command) 875 } 876 if pe.PostExecuteContainerID != "1234" { 877 t.Errorf("PostExecutor not called with expected ID: %s", 878 pe.PostExecuteContainerID) 879 } 880 if !reflect.DeepEqual(ro.Env, expectedEnv) { 881 t.Errorf("Unexpected container environment passed to RunContainer: %v, should be %v", ro.Env, expectedEnv) 882 } 883 if !reflect.DeepEqual(pe.PostExecuteDestination, "test-command") { 884 t.Errorf("PostExecutor not called with expected command: %s", pe.PostExecuteDestination) 885 } 886 } 887 888 func TestExecuteRunContainerError(t *testing.T) { 889 rh := newFakeSTI(&FakeSTI{}) 890 fd := rh.docker.(*docker.FakeDocker) 891 runContainerError := fmt.Errorf("an error") 892 fd.RunContainerError = runContainerError 893 err := rh.Execute("test-command", "", rh.config) 894 if err != runContainerError { 895 t.Errorf("Did not get expected error, got %v", err) 896 } 897 } 898 899 func TestExecuteErrorCreateTarFile(t *testing.T) { 900 rh := newFakeSTI(&FakeSTI{}) 901 rh.tar.(*test.FakeTar).CreateTarError = errors.New("CreateTarError") 902 err := rh.Execute("test-command", "", rh.config) 903 if err == nil || err.Error() != "CreateTarError" { 904 t.Errorf("An error was expected for CreateTarFile, but got different: %#v", err) 905 } 906 } 907 908 func TestCleanup(t *testing.T) { 909 rh := newFakeBaseSTI() 910 911 rh.config.WorkingDir = "/working-dir" 912 preserve := []bool{false, true} 913 for _, p := range preserve { 914 rh.config.PreserveWorkingDir = p 915 rh.fs = &testfs.FakeFileSystem{} 916 rh.garbage = build.NewDefaultCleaner(rh.fs, rh.docker) 917 rh.garbage.Cleanup(rh.config) 918 removedDir := rh.fs.(*testfs.FakeFileSystem).RemoveDirName 919 if p && removedDir != "" { 920 t.Errorf("Expected working directory to be preserved, but it was removed.") 921 } else if !p && removedDir == "" { 922 t.Errorf("Expected working directory to be removed, but it was preserved.") 923 } 924 } 925 } 926 927 func TestNewWithInvalidExcludeRegExp(t *testing.T) { 928 _, err := New(nil, &api.Config{ 929 DockerConfig: docker.GetDefaultDockerConfig(), 930 ExcludeRegExp: "(", 931 }, nil, build.Overrides{}) 932 if syntaxErr, ok := err.(*syntax.Error); ok && syntaxErr.Code != syntax.ErrMissingParen { 933 t.Errorf("expected regexp compilation error, got %v", err) 934 } 935 }