// Tests the Start command. func TestStart(t *testing.T) { ctx, tempDir := NewTestContext(t) defer cleanTestDir(t, tempDir) const testPipeline = "test-start" const testBundle = "bazbux" createTestBundleAndPipeline(t, ctx, testPipeline, testBundle, tempDir) defer shell.RunAndLog("docker", "rmi", ctx.GetImage(testBundle)) defer shell.RunAndLog("docker", "rmi", ctx.GetManagerImage()) // set the interrupt handler to go off after 5 seconds go func() { time.Sleep(5 * time.Second) syscall.Kill(syscall.Getpid(), syscall.SIGINT) }() // send a post request to the "server" and see what we get back go func() { time.Sleep(4 * time.Second) hostIp, err := ctx.GetDockerHost() if err != nil { t.Errorf("TestStart: Got an error getting the docker host: '%v'", err) } resp, err := http.Post(fmt.Sprintf("http://%s:9800", hostIp), "application/json", bytes.NewBufferString(`{"a": "trusty"}`)) if err != nil { t.Error(err) } buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) result := buf.String() if result != `{"a": "trusty", "b": "echo trusty"}` { t.Errorf("TestStart: Got '%s'; did not get expected response", result) } }() // start the pipeline locally (set the gce project to '' to run // locally) err := ctx.Start(testPipeline, "") if err != nil { t.Errorf("TestStart: '%v'", err) } // now attempt to start it remotely const projectId = "gce-project-id" err = ctx.Start(testPipeline, projectId) if err != nil { t.Errorf("TestStart: [remote] '%v'", err) } remoteImage := fmt.Sprintf("gcr.io/%s/plumber-%s", projectId, "manager") defer shell.RunAndLog("docker", "rmi", remoteImage) remoteImage = fmt.Sprintf("gcr.io/%s/plumber-%s", projectId, testBundle) defer shell.RunAndLog("docker", "rmi", remoteImage) }
// Bundle stuff... // BUG(echu): need to figure out how to handle conflicts in bundle names func (ctx *Context) Bundle(bundlePath string) error { log.Printf("==> Creating bundle from '%s'", bundlePath) defer log.Printf("<== Bundling complete.") log.Printf(" | Parsing bundle config.") bundleConfig, err := ParseBundleFromDir(bundlePath) if err != nil { return err } log.Printf(" %v", bundleConfig) log.Printf(" | Making temp file for python wrapper") wrapper, err := ioutil.TempFile(bundlePath, "plumber") defer removeTempFile(wrapper) if err != nil { return err } log.Printf(" Created '%s'", wrapper.Name()) templateCtx := templateContext{ Wrapper: path.Base(wrapper.Name()), Plumber: bundleConfig, } log.Printf(" | Writing wrapper.") tmpl, err := template.New("wrapper").Parse(wrapperTemplate) if err != nil { return err } if err := tmpl.Execute(wrapper, templateCtx); err != nil { return err } log.Printf(" Done.") log.Printf(" | Making temp file for Dockerfile") dockerfile, err := ioutil.TempFile(bundlePath, "plumber") defer removeTempFile(dockerfile) if err != nil { return err } log.Printf(" Created '%s'", dockerfile.Name()) log.Printf(" | Writing Dockerfile.") tmpl, err = template.New("dockerfile").Parse(dockerfileTemplate) if err != nil { return err } if err := tmpl.Execute(dockerfile, templateCtx); err != nil { return err } log.Printf(" Done.") log.Printf(" | Building container.") err = shell.RunAndLog(ctx.DockerCmd, "build", "--pull", "-t", ctx.GetImage(bundleConfig.Name), "-f", dockerfile.Name(), bundlePath) if err != nil { return err } log.Printf(" Container '%s' built.", ctx.GetImage(bundleConfig.Name)) return nil }
func (ctx *Context) Create(name string) error { // creates a pipeline by initializing a git repo at ~/.plumb/<NAME> log.Printf("==> Creating '%s' pipeline", name) defer log.Printf("<== Creation complete.") if name == "" { return errors.New("Cannot create a pipeline with no name.") } log.Printf(" | Making directory") // note that we use PipelinePath instead of GetPipeline here; this // is because we only need the path to create it path := ctx.PipelinePath(name) // if the path already exists, give an error if _, err := os.Stat(path); err == nil { return errors.New("Pipeline already exists.") } if err := os.MkdirAll(path, 0755); err != nil { return err } log.Printf(" Created pipeline directory at '%s'", path) log.Printf(" | Initializing pipeline with git") if err := shell.RunAndLog("git", "init", path); err != nil { return err } log.Printf(" Done.") return nil }
func addOne(ctx *Context, pipeline string, bundle string) error { log.Printf(" | Adding '%s' to '%s'.", bundle, pipeline) defer log.Printf(" Added '%s'.", bundle) path, err := ctx.GetPipeline(pipeline) if err != nil { return err } log.Printf(" | Parsing bundle config.") bundleConfig, err := ParseBundleFromDir(bundle) if err != nil { return err } log.Printf(" Done.") log.Printf(" | Copying `.plumber.yml` config to `%s.yml`.", bundleConfig.Name) config := fmt.Sprintf("%s/%s.yml", path, bundleConfig.Name) bytes, err := yaml.Marshal(&bundleConfig) if err != nil { return err } if err := ioutil.WriteFile(config, bytes, 0644); err != nil { return err } log.Printf(" Done.") log.Printf(" | Adding `%s.yml` to version control.", bundleConfig.Name) if err := shell.RunAndLog("git", "-C", path, "add", config); err != nil { return err } message := fmt.Sprintf("Updated '%s' config.", bundleConfig.Name) if err := shell.RunAndLog("git", "-C", path, "commit", "-m", message, "--author", "\"Plumber Bot <*****@*****.**>\""); err != nil { return err } return nil }
func TestInterrupt(t *testing.T) { // set the interrupt handler to go off after 50 milliseconds go func() { time.Sleep(50 * time.Millisecond) syscall.Kill(syscall.Getpid(), syscall.SIGINT) }() err := shell.RunAndLog("/bin/bash", "-c", "while true; do true; done") if err == nil || err.Error() != "signal: interrupt" { t.Error("Should've received a SIGINT") } }
// this is a *functional test* // we check that boostrap works by actually running the boostrap command // and checking that the container is built and runs func TestBootstrap(t *testing.T) { ctx, tempDir := NewTestContext(t) defer cleanTestDir(t, tempDir) // step 1. remove any image named plumber/test-manager from the current // set of docker images (ignore any errors) _ = shell.RunAndLog(ctx.DockerCmd, "rmi", ctx.GetManagerImage()) // step 2. invoke Bootstrap for building ctx.GetManagerImage() if err := ctx.Bootstrap(); err != nil { t.Errorf("Bootstrap: Got an error during bootstrap: '%v'", err) } defer shell.RunAndLog(ctx.DockerCmd, "rmi", ctx.GetManagerImage()) // step 3. run the image (it *should* just echo in response) if err := shell.RunAndLog(ctx.DockerCmd, "run", "-d", "-p", "9800:9800", "--name", "plumber-test", ctx.GetManagerImage()); err != nil { t.Errorf("Bootstrap: Got an error during docker run: '%v'", err) } defer shell.RunAndLog(ctx.DockerCmd, "rm", "-f", "plumber-test") // wait a bit for the container to come up time.Sleep(1 * time.Second) // step 4. send some JSON and check for echos hostIp, err := ctx.GetDockerHost() if err != nil { t.Errorf("Bootstrap: Got an error getting the docker host: '%v'", err) } // second, send over some JSON and verify result resp, err := http.Post(fmt.Sprintf("http://%s:9800", hostIp), "application/json", bytes.NewBufferString(`{"foo": 3}`)) if err != nil { t.Error(err) } buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) result := buf.String() if result != `{"foo": 3}` { t.Errorf("Bootstrap: Got '%s'; did not get expected response", result) } }
func (ctx *Context) Bootstrap() error { // use docker to compile the manager and copy the binary into // another docker container // // tag this new container log.Printf("==> Bootstraping plumb.") defer log.Printf("<== Bootstrap complete.") log.Printf(" | Creating temp directory.") if err := os.MkdirAll(ctx.BootstrapDir, 0755); err != nil { return err } defer func() { if err := os.RemoveAll(ctx.BootstrapDir); err != nil { panic(err) } }() log.Printf(" Temp directory created at '%s'", ctx.BootstrapDir) log.Printf(" | Writing manager source files.") if err := writeAsset("manager.go", ctx.BootstrapDir); err != nil { return err } if err := writeAsset("Dockerfile", ctx.BootstrapDir); err != nil { return err } if err := writeAsset("README.md", ctx.BootstrapDir); err != nil { return err } if err := writeAsset("manager_test.go", ctx.BootstrapDir); err != nil { return err } log.Printf(" Done") if err := shell.RunAndLog(ctx.DockerCmd, "run", "--rm", "-v", "/var/run/docker.sock:/var/run/docker.sock", "-v", fmt.Sprintf("%s:/src", ctx.BootstrapDir), "centurylink/golang-builder", ctx.GetManagerImage()); err != nil { return err } return nil }
func BenchmarkRunAndLog(b *testing.B) { log.SetOutput(ioutil.Discard) for i := 0; i < b.N; i++ { shell.RunAndLog("echo", "true") } }
func TestRunAndLogFails(t *testing.T) { err := shell.RunAndLog("-notlikely-to-be-a*-cmd") if err == nil { t.Error("Expected an error but never got one!") } }
func TestRunAndLog(t *testing.T) { err := shell.RunAndLog("true") if err != nil { t.Error(err) } }