func TestBuilderEnsureContainer(t *testing.T) { t.Skip() // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, Auth: &docker.AuthConfiguration{}, } containerConfig := &docker.Config{ Image: "grammarly/rsync-static:1", } containerName := "rocker_TestBuilderEnsureContainer" defer func() { if err := client.RemoveContainer(docker.RemoveContainerOptions{ID: containerName, Force: true}); err != nil { t.Fatal(err) } }() if _, err := builder.ensureContainer(containerName, containerConfig, "testing"); err != nil { t.Fatal(err) } assert.Equal(t, "", "") }
func TestBuilderBuildInclude(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildInclude_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/nodejs": ` RUN touch /test/bin/nodejs RUN touch /test/bin/npm `, "/java": ` RUN touch /test/bin/java RUN touch /test/bin/gradle `, "/Rockerfile": ` FROM busybox:buildroot-2013.08.1 RUN mkdir -p /test/bin INCLUDE nodejs INCLUDE java CMD ["ls", "/test/bin"] `, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, }, nil) t.Logf("Got result: %q", result) assert.Equal(t, "gradle\njava\nnodejs\nnpm\n", result, "expected result (ls) to contain included files") }
func TestBuilderBuildRequire(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildRequire_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 REQUIRE version RUN echo "$version" > /testing CMD cat /testing`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } run := func(vars []string) (string, error) { tlpVars, err := template.VarsFromStrings(vars) if err != nil { return "", err } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, Vars: tlpVars, } imageID, err := builder.Build() if err != nil { return "", err } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() return runContainer(t, client, &docker.Config{ Image: imageID, }, nil) } _, err1 := run([]string{}) result, err2 := run([]string{"version=123"}) assert.Equal(t, "Var $version is required but not set", err1.Error()) assert.Nil(t, err2, "expected second run to not give error") assert.Equal(t, "123\n", result) }
func TestBuilderBuildVars(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildVars_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 RUN echo "version:$version" > /version`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } vars, err := template.VarsFromStrings([]string{"version=125"}) if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Vars: vars, // Push: true, Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, Cmd: []string{"cat", "/version"}, }, nil) assert.Equal(t, "version:125\n", result, "failed") }
func TestBuilderMountFromHost(t *testing.T) { wd, err := os.Getwd() if err != nil { t.Fatal(err) } // Use current working directroy as a temp dir to make MOUNT work in boot2docker tempDir, err := ioutil.TempDir(wd, "rocker_TestBuilderMountFromHost_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 MOUNT .:/src RUN echo "hello" > /src/test`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() content, err := ioutil.ReadFile(tempDir + "/test") if err != nil { t.Fatal(err) } assert.Equal(t, "hello\n", string(content)) }
func TestBuilderBuildTag(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildTag_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 TAG testing RUN touch /testing PUSH quay.io/testing_project`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, Cmd: []string{"ls", "/"}, }, nil) t.Logf("Got result: %s", result) assert.Equal(t, "true", "true", "failed") }
func TestBuilderBuildGitWarning(t *testing.T) { t.Skip() tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildGitWarning_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/.git/HEAD": "hello", "/testing": "hello2", "/Rockerfile": `FROM busybox:buildroot-2013.08.1 ADD . /`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, }, nil) t.Logf("Got result: %q", result) assert.Contains(t, result, "testing", "expected result (ls) to contain testing file") }
func TestBuilderBuildSemverTag(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildSemverTag_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM scratch TAG --semver testing:1.2.3-build123`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, // Vars: VarsFromStrings([]string{"branch=master", "commit=314ad"}), } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, Cmd: []string{"ls", "/"}, }, nil) t.Logf("Got result: %s", result) assert.Equal(t, "true", "true", "failed") }
func TestBuilderBuildAttach(t *testing.T) { t.Skip() tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildAttach_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 CMD ["/bin/sh"] ATTACH --name=test-attach ["ls", "-la"]`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", InStream: os.Stdin, OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, Attach: true, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() }
func TestBuilderEnsureImage(t *testing.T) { t.Skip() // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, Auth: &docker.AuthConfiguration{}, } image := "busybox:buildroot-2013.08.1" if err := builder.ensureImage(image, "testing"); err != nil { t.Fatal(err) } assert.Equal(t, "", "") }
func TestBuilderBuildVar(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildVar_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 VAR test=true RUN touch /testing RUN if [ "$test" == "true" ] ; then echo "done test" > /testing; fi CMD cat /testing`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } run := func(vars []string) (string, error) { tplVars, err := template.VarsFromStrings(vars) if err != nil { return "", err } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, Vars: tplVars, } imageID, err := builder.Build() if err != nil { return "", err } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() return runContainer(t, client, &docker.Config{ Image: imageID, }, nil) } result1, err := run([]string{}) if err != nil { t.Fatal(err) } result2, err := run([]string{"test=false"}) if err != nil { t.Fatal(err) } assert.Equal(t, "done test\n", result1) assert.Equal(t, "", result2) }
func TestBuilderImportFromScratch(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderImportFromScratch_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": ` FROM busybox:buildroot-2013.08.1 RUN mkdir -p /zzz && echo "hi" > /zzz/lalala EXPORT zzz / FROM scratch IMPORT zzz / CMD ["true"] `, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() // Create data volume container with scratch image c, err := client.CreateContainer(docker.CreateContainerOptions{ Config: &docker.Config{ Image: imageID, Volumes: map[string]struct{}{ "/zzz": struct{}{}, }, }, }) if err != nil { t.Fatal(err) } defer func() { if err := client.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID, RemoveVolumes: true, Force: true}); err != nil { t.Log(err) } }() result, err := runContainer(t, client, &docker.Config{ Image: "busybox:buildroot-2013.08.1", Cmd: []string{"/bin/sh", "-c", "cat /zzz/lalala"}, }, &docker.HostConfig{ VolumesFrom: []string{c.ID}, }) t.Logf("Got result: %q", result) assert.Equal(t, "hi\n", result) }
func TestBuilderBuildContainerVolume(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildContainerVolume_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 MOUNT /cache RUN echo "hello" >> /cache/output.log RUN cp /cache/output.log /result_cache.log CMD cat /result_cache.log`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } // Step 1 runUtilizeCache := func(utilizeCache bool) (result string, err error) { builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), UtilizeCache: utilizeCache, Docker: client, } imageID, err := builder.Build() if err != nil { return "", err } t.Logf("Got imageID: %s", imageID) // Cleanup mount containers defer func() { for _, mountContainerID := range builder.getAllMountContainerIds() { if err := client.RemoveContainer(docker.RemoveContainerOptions{ ID: mountContainerID, RemoveVolumes: true, Force: true, }); err != nil { t.Log(err) } } }() defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() // Step 2 builder2 := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), UtilizeCache: utilizeCache, Docker: client, } imageID2, err := builder2.Build() if err != nil { return "", err } t.Logf("Got imageID2: %s", imageID2) defer func() { if err := client.RemoveImageExtended(imageID2, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() return runContainer(t, client, &docker.Config{ Image: imageID2, }, nil) } result1, err := runUtilizeCache(true) if err != nil { t.Fatal(err) } assert.Equal(t, "hello\n", result1, "failed") result2, err := runUtilizeCache(false) if err != nil { t.Fatal(err) } assert.Equal(t, "hello\nhello\n", result2, "failed") }
func TestBuilderBuildMultiple(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildMultiple_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/index.js": "console.log('hello')", "/data/README": "hello", "/Rockerfile": ` FROM busybox:buildroot-2013.08.1 ADD . /app MOUNT /app/node_modules RUN echo "hehe" > /app/node_modules/some_module && \ cd /app/node_modules && \ ln -sf some_module link_to_some_module EXPORT /app FROM busybox:buildroot-2013.08.1 IMPORT /app `, }) if err != nil { t.Fatal(err) } imageIDs := make(map[string]struct{}) mounts := make(map[string]struct{}) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } run := func() (imageID string, err error) { builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", UtilizeCache: true, OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } defer func() { for _, mountContainerID := range builder.getAllMountContainerIds() { if mountContainerID != "" { mounts[mountContainerID] = struct{}{} } } }() imageID, err = builder.Build() if err != nil { return "", err } t.Logf("Got imageID: %s", imageID) imageIDs[imageID] = struct{}{} for _, imageID := range builder.intermediateImages { imageIDs[imageID] = struct{}{} } return imageID, nil } defer func() { for imageID := range imageIDs { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } } }() // Cleanup mount containers defer func() { for mountContainerID := range mounts { if err := client.RemoveContainer(docker.RemoveContainerOptions{ ID: mountContainerID, RemoveVolumes: true, Force: true, }); err != nil { t.Log(err) } } }() imageID1, err := run() if err != nil { t.Fatal(err) } fmt.Println("============================================================") imageID2, err := run() if err != nil { t.Fatal(err) } assert.Equal(t, imageID1, imageID2, "expected images to be equal (valid caching behavior)") }
func TestBuilderBuildMounts(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildTag_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": `FROM busybox:buildroot-2013.08.1 MOUNT /app/node_modules /app/bower_components RUN ls /app > /out CMD cat /out`, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() // Cleanup mount containers defer func() { for _, mountContainerID := range builder.getAllMountContainerIds() { if err := client.RemoveContainer(docker.RemoveContainerOptions{ ID: mountContainerID, RemoveVolumes: true, Force: true, }); err != nil { t.Log(err) } } }() result, err := runContainer(t, client, &docker.Config{ Image: imageID, }, nil) t.Logf("Got result: %s", result) assert.Equal(t, "bower_components\nnode_modules\n", result, "expected both volumes to be mounted") assert.Equal(t, 1, len(builder.getMountContainerIds()), "expected only one volume container to be created") }
func TestBuilderBuildTagLabels(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildTagLabels_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) rockerfileContent := `FROM busybox:buildroot-2013.08.1 TAG testing RUN touch /testing LABEL foo=bar PUSH quay.io/testing_project` err = test.MakeFiles(tempDir, map[string]string{ "/Rockerfile": rockerfileContent, }) // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } vars, err := template.VarsFromStrings([]string{"asd=qwe"}) if err != nil { t.Fatal(err) } builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", OutStream: util.PrefixPipe("[TEST] ", os.Stdout), CliVars: vars, Docker: client, AddMeta: true, } imageID, err := builder.Build() if err != nil { t.Fatal(err) } t.Logf("Got imageID: %s", imageID) defer func() { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } }() inspect, err := client.InspectImage(imageID) if err != nil { t.Fatal(err) } // test inherited labels assert.Equal(t, "bar", inspect.Config.Labels["foo"]) // test rockerfile content data := &RockerImageData{} if err := json.Unmarshal([]byte(inspect.Config.Labels["rocker-data"]), data); err != nil { t.Fatal(err) } assert.Equal(t, rockerfileContent, data.Rockerfile) // test vars assert.Equal(t, vars, data.Vars) }
func TestBuilderBuildAddCache(t *testing.T) { tempDir, err := ioutil.TempDir("/tmp", "rocker_TestBuilderBuildAddCache_") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) err = test.MakeFiles(tempDir, map[string]string{ "/data/README": "hello", "/Rockerfile": ` FROM busybox:buildroot-2013.08.1 ADD . /src RUN ls -la /src `, }) if err != nil { t.Fatal(err) } var imageIDs []string // we will need docker client to cleanup and do some cross-checks client, err := dockerclient.New() if err != nil { t.Fatal(err) } run := func() (imageID string, err error) { builder := &Builder{ Rockerfile: tempDir + "/Rockerfile", UtilizeCache: true, OutStream: util.PrefixPipe("[TEST] ", os.Stdout), Docker: client, } imageID, err = builder.Build() if err != nil { return "", err } t.Logf("Got imageID: %s", imageID) imageIDs = append(imageIDs, imageID) return imageID, nil } defer func() { for _, imageID := range imageIDs { if err := client.RemoveImageExtended(imageID, docker.RemoveImageOptions{Force: true}); err != nil { t.Log(err) } } }() imageID1, err := run() if err != nil { t.Fatal(err) } time.Sleep(time.Second) imageID2, err := run() if err != nil { t.Fatal(err) } assert.Equal(t, imageID1, imageID2, "expected images to be equal (valid caching behavior)") }
func (builder *Builder) runContainerAttachStdin(containerID string, attachStdin bool) error { success := make(chan struct{}) attachOpts := docker.AttachToContainerOptions{ Container: containerID, OutputStream: util.PrefixPipe("[Docker] ", builder.OutStream), ErrorStream: util.PrefixPipe("[Docker] ", builder.OutStream), Stdout: true, Stderr: true, Stream: true, Success: success, } if attachStdin { if !builder.isTerminalIn { return fmt.Errorf("Cannot attach to a container on non tty input") } oldState, err := term.SetRawTerminal(builder.fdIn) if err != nil { return err } defer term.RestoreTerminal(builder.fdIn, oldState) attachOpts.InputStream = readerVoidCloser{builder.InStream} attachOpts.OutputStream = builder.OutStream attachOpts.ErrorStream = builder.OutStream attachOpts.Stdin = true attachOpts.RawTerminal = true } finished := make(chan struct{}, 1) go func() { if err := builder.Docker.AttachToContainer(attachOpts); err != nil { select { case <-finished: // Ignore any attach errors when we have finished already. // It may happen if we attach stdin, then container exit, but then there is other input from stdin continues. // This is the case when multiple ATTACH command are used in a single Rockerfile. // The problem though is that we cannot close stdin, to have it available for the subsequent ATTACH; // therefore, hijack goroutine from the previous ATTACH will hang until the input received and then // it will fire an error. // It's ok for `rocker` since it is not a daemon, but rather a one-off command. // // Also, there is still a problem that `rocker` loses second character from the Stdin in a second ATTACH. // But let's consider it a corner case. default: // Print the error. We cannot return it because the main routine is handing on WaitContaienr fmt.Fprintf(builder.OutStream, "Got error while attaching to container %s: %s\n", containerID, err) } } }() success <- <-success if err := builder.Docker.StartContainer(containerID, &docker.HostConfig{}); err != nil { return err } if attachStdin { if err := builder.monitorTtySize(containerID); err != nil { return fmt.Errorf("Failed to monitor TTY size for container %s, error: %s", containerID, err) } } sigch := make(chan os.Signal, 1) signal.Notify(sigch, os.Interrupt) errch := make(chan error) go func() { statusCode, err := builder.Docker.WaitContainer(containerID) if err != nil { errch <- err } else if statusCode != 0 { errch <- fmt.Errorf("Failed to run container, exit with code %d", statusCode) } errch <- nil return }() select { case err := <-errch: // indicate 'finished' so the `attach` goroutine will not give any errors finished <- struct{}{} if err != nil { return err } case <-sigch: fmt.Fprintf(builder.OutStream, "[Rocker] Received SIGINT, remove current container...\n") if err := builder.removeContainer(containerID); err != nil { fmt.Fprintf(builder.OutStream, "[Rocker] Failed to remove container: %s\n", err) } // TODO: send signal to builder.Build() and have a proper cleanup os.Exit(2) } return nil }