Example #1
0
func testApp(s *CLISuite, t *c.C, remote string) {
	app := s.newGitRepo(t, "")
	name := random.String(30)
	flynnRemote := fmt.Sprintf("%s\tssh://git@%s/%s.git (push)", remote, s.clusterConf(t).GitHost, name)

	if remote == "flynn" {
		t.Assert(app.flynn("create", "-y", name), Outputs, fmt.Sprintf("Created %s\n", name))
	} else {
		t.Assert(app.flynn("create", "-r", remote, "-y", name), Outputs, fmt.Sprintf("Created %s\n", name))
	}
	t.Assert(app.flynn("apps"), SuccessfulOutputContains, name)
	t.Assert(app.flynn("-c", "default", "apps"), SuccessfulOutputContains, name)
	if remote == "" {
		t.Assert(app.git("remote", "-v"), c.Not(SuccessfulOutputContains), flynnRemote)
	} else {
		t.Assert(app.git("remote", "-v"), SuccessfulOutputContains, flynnRemote)
	}

	// make sure flynn components are listed
	t.Assert(app.flynn("apps"), SuccessfulOutputContains, "router")
	t.Assert(app.flynn("-c", "default", "apps"), SuccessfulOutputContains, "router")

	// flynn delete
	if remote == "flynn" {
		t.Assert(app.flynn("delete", "--yes"), Succeeds)
	} else {
		if remote == "" {
			t.Assert(app.flynn("-a", name, "delete", "--yes", "-r", remote), Succeeds)
		} else {
			t.Assert(app.flynn("delete", "--yes", "-r", remote), Succeeds)
		}
	}
	t.Assert(app.git("remote", "-v"), c.Not(SuccessfulOutputContains), flynnRemote)
}
Example #2
0
func (s *CLISuite) TestKey(t *c.C) {
	app := s.newGitRepo(t, "empty")
	t.Assert(app.flynn("create"), Succeeds)

	t.Assert(app.flynn("key", "add", s.sshKeys(t).Pub), Succeeds)

	// calculate fingerprint
	data, err := ioutil.ReadFile(s.sshKeys(t).Pub)
	t.Assert(err, c.IsNil)
	pubKey, _, _, _, err := ssh.ParseAuthorizedKey(data)
	t.Assert(err, c.IsNil)
	digest := md5.Sum(pubKey.Marshal())
	fingerprint := formatKeyID(hex.EncodeToString(digest[:]))

	t.Assert(app.flynn("key"), SuccessfulOutputContains, fingerprint)

	t.Assert(app.git("commit", "--allow-empty", "-m", "should succeed"), Succeeds)
	t.Assert(app.git("push", "flynn", "master"), Succeeds)

	t.Assert(app.flynn("key", "remove", fingerprint), Succeeds)
	t.Assert(app.flynn("key"), c.Not(SuccessfulOutputContains), fingerprint)

	t.Assert(app.git("commit", "--allow-empty", "-m", "should fail"), Succeeds)
	t.Assert(app.git("push", "flynn", "master"), c.Not(Succeeds))

	t.Assert(app.flynn("delete", "--yes"), Succeeds)
}
Example #3
0
func (s *CLISuite) TestMeta(t *c.C) {
	app := s.newCliTestApp(t)
	t.Assert(app.flynn("meta", "set", "META_TEST=var", "SECOND_VAL=2"), Succeeds)
	t.Assert(app.flynn("meta").Output, Matches, `META_TEST *var`)
	t.Assert(app.flynn("meta").Output, Matches, `SECOND_VAL *2`)
	// test that unset can remove all meta tags
	t.Assert(app.flynn("meta", "unset", "META_TEST", "SECOND_VAL"), Succeeds)
	t.Assert(app.flynn("meta").Output, c.Not(Matches), `META_TEST *var`)
	t.Assert(app.flynn("meta").Output, c.Not(Matches), `SECOND_VAL *2`)
}
Example #4
0
func (s *CLISuite) TestCluster(t *c.C) {
	// use a custom flynnrc to avoid disrupting other tests
	file, err := ioutil.TempFile("", "")
	t.Assert(err, c.IsNil)
	flynn := func(cmdArgs ...string) *CmdResult {
		cmd := exec.Command(args.CLI, cmdArgs...)
		cmd.Env = flynnEnv(file.Name())
		return run(t, cmd)
	}

	// cluster add
	t.Assert(flynn("cluster", "add", "--no-git", "foo", "https://controller.foo.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "foo")
	t.Assert(flynn("cluster", "add", "--no-git", "-p", "KGCENkp53YF5OvOKkZIry71+czFRkSw2ZdMszZ/0ljs=", "test", "https://controller.test.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "test")
	t.Assert(flynn("cluster", "add", "-f", "--no-git", "-p", "KGCENkp53YF5OvOKkZIry71+czFRkSw2ZdMszZ/0ljs=", "test", "https://controller.test.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "test")
	t.Assert(flynn("cluster", "add", "-f", "-d", "--no-git", "-p", "KGCENkp53YF5OvOKkZIry71+czFRkSw2ZdMszZ/0ljs=", "test", "https://controller.test.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "test")
	// make sure the cluster is present in the config
	cfg, err := config.ReadFile(file.Name())
	t.Assert(err, c.IsNil)
	t.Assert(cfg.Default, c.Equals, "test")
	t.Assert(cfg.Clusters, c.HasLen, 2)
	t.Assert(cfg.Clusters[0].Name, c.Equals, "foo")
	t.Assert(cfg.Clusters[1].Name, c.Equals, "test")
	// overwriting with a conflicting name and a different conflicting url should error
	conflict := flynn("cluster", "add", "-f", "--no-git", "foo", "https://controller.test.example.com", "e09dc5301d72be755a3d666f617c4600")
	t.Assert(conflict, c.Not(Succeeds))
	t.Assert(conflict, OutputContains, "conflict with")
	// overwriting (without --force) should not work
	t.Assert(flynn("cluster", "add", "test", "foo", "bar"), c.Not(Succeeds))
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "test")
	t.Assert(flynn("cluster"), SuccessfulOutputContains, "(default)")
	// change default cluster
	t.Assert(flynn("cluster", "default", "test"), SuccessfulOutputContains, "\"test\" is now the default cluster.")
	t.Assert(flynn("cluster", "default", "missing"), OutputContains, "Cluster \"missing\" does not exist and cannot be set as default.")
	t.Assert(flynn("cluster", "default"), SuccessfulOutputContains, "test")
	cfg, err = config.ReadFile(file.Name())
	t.Assert(err, c.IsNil)
	t.Assert(cfg.Default, c.Equals, "test")
	// cluster remove
	t.Assert(flynn("cluster", "remove", "test"), Succeeds)
	t.Assert(flynn("cluster"), c.Not(SuccessfulOutputContains), "test")
	cfg, err = config.ReadFile(file.Name())
	t.Assert(err, c.IsNil)
	t.Assert(cfg.Clusters, c.HasLen, 1)
	t.Assert(flynn("cluster", "remove", "foo"), Succeeds)
	// cluster remove default and set next available
	t.Assert(flynn("cluster", "add", "-d", "--no-git", "-p", "KGCENkp53YF5OvOKkZIry71+czFRkSw2ZdMszZ/0ljs=", "test", "https://controller.test.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster", "add", "--no-git", "-p", "KGCENkp53YF5OvOKkZIry71+czFRkSw2ZdMszZ/0ljs=", "next", "https://controller.next.example.com", "e09dc5301d72be755a3d666f617c4600"), Succeeds)
	t.Assert(flynn("cluster", "remove", "test"), SuccessfulOutputContains, "Cluster \"test\" removed and \"next\" is now the default cluster.")
	t.Assert(flynn("cluster", "default"), SuccessfulOutputContains, "next")
}
Example #5
0
func (s *CLISuite) TestRelease(t *c.C) {
	releaseJSON := []byte(`{
		"env": {"GLOBAL": "FOO"},
		"processes": {
			"echoer": {
				"cmd": ["/bin/echoer"],
				"env": {"ECHOER_ONLY": "BAR"}
			},
			"env": {
				"cmd": ["sh", "-c", "env; while true; do sleep 60; done"],
				"env": {"ENV_ONLY": "BAZ"}
			}
		}
	}`)
	release := &ct.Release{}
	t.Assert(json.Unmarshal(releaseJSON, &release), c.IsNil)
	for typ, proc := range release.Processes {
		resource.SetDefaults(&proc.Resources)
		release.Processes[typ] = proc
	}

	file, err := ioutil.TempFile("", "")
	t.Assert(err, c.IsNil)
	file.Write(releaseJSON)
	file.Close()

	app := s.newCliTestApp(t)
	defer app.cleanup()
	t.Assert(app.flynn("release", "add", "-f", file.Name(), imageURIs["test-apps"]), Succeeds)

	r, err := s.controller.GetAppRelease(app.name)
	t.Assert(err, c.IsNil)
	t.Assert(r.Env, c.DeepEquals, release.Env)
	t.Assert(r.Processes, c.DeepEquals, release.Processes)

	scaleCmd := app.flynn("scale", "--no-wait", "env=1", "foo=1")
	t.Assert(scaleCmd, c.Not(Succeeds))
	t.Assert(scaleCmd, OutputContains, "ERROR: unknown process types: \"foo\"")

	// create a job watcher for the new release
	watcher, err := s.controllerClient(t).WatchJobEvents(app.name, r.ID)
	t.Assert(err, c.IsNil)
	defer watcher.Close()

	scaleCmd = app.flynn("scale", "--no-wait", "env=1")
	t.Assert(watcher.WaitFor(ct.JobEvents{"env": {ct.JobStateUp: 1}}, scaleTimeout, nil), c.IsNil)
	envLog := app.flynn("log")
	t.Assert(envLog, Succeeds)
	t.Assert(envLog, SuccessfulOutputContains, "GLOBAL=FOO")
	t.Assert(envLog, SuccessfulOutputContains, "ENV_ONLY=BAZ")
	t.Assert(envLog, c.Not(SuccessfulOutputContains), "ECHOER_ONLY=BAR")
}
Example #6
0
func (s *SchedulerSuite) TestControllerRestart(t *c.C) {
	// get the current controller details
	app, err := s.controllerClient(t).GetApp("controller")
	t.Assert(err, c.IsNil)
	release, err := s.controllerClient(t).GetAppRelease("controller")
	t.Assert(err, c.IsNil)
	formation, err := s.controllerClient(t).GetFormation(app.ID, release.ID)
	t.Assert(err, c.IsNil)
	list, err := s.controllerClient(t).JobList("controller")
	t.Assert(err, c.IsNil)
	var jobs []*ct.Job
	for _, job := range list {
		if job.Type == "web" && job.State == "up" {
			jobs = append(jobs, job)
		}
	}
	t.Assert(jobs, c.HasLen, 2)
	hostID, jobID, _ := cluster.ParseJobID(jobs[0].ID)
	t.Assert(hostID, c.Not(c.Equals), "")
	t.Assert(jobID, c.Not(c.Equals), "")
	debugf(t, "current controller app[%s] host[%s] job[%s]", app.ID, hostID, jobID)

	// start another controller and wait for it to come up
	watcher, err := s.controllerClient(t).WatchJobEvents("controller", release.ID)
	t.Assert(err, c.IsNil)
	defer watcher.Close()
	debug(t, "scaling the controller up")
	formation.Processes["web"]++
	t.Assert(s.controllerClient(t).PutFormation(formation), c.IsNil)
	err = watcher.WaitFor(ct.JobEvents{"web": {"up": 1}}, scaleTimeout, nil)
	t.Assert(err, c.IsNil)

	// kill the first controller and check the scheduler brings it back online
	cc := cluster.NewClientWithServices(s.discoverdClient(t).Service)
	hc, err := cc.Host(hostID)
	t.Assert(err, c.IsNil)
	debug(t, "stopping job ", jobID)
	t.Assert(hc.StopJob(jobID), c.IsNil)
	err = watcher.WaitFor(ct.JobEvents{"web": {"down": 1, "up": 1}}, scaleTimeout, nil)
	t.Assert(err, c.IsNil)

	// scale back down
	debug(t, "scaling the controller down")
	formation.Processes["web"]--
	t.Assert(s.controllerClient(t).PutFormation(formation), c.IsNil)
	err = watcher.WaitFor(ct.JobEvents{"web": {"down": 1}}, scaleTimeout, nil)
	t.Assert(err, c.IsNil)

	// unset the suite's client so other tests use a new client
	s.controller = nil
}
Example #7
0
func (s *CLISuite) TestRun(t *c.C) {
	app := s.newCliTestApp(t)
	defer app.cleanup()

	// this shouldn't be logged
	t.Assert(app.sh("echo foo"), Outputs, "foo\n")
	// drain the events
	app.waitFor(ct.JobEvents{"": {ct.JobStateUp: 1, ct.JobStateDown: 1}})

	// this should be logged due to the --enable-log flag
	t.Assert(app.flynn("run", "--enable-log", "echo", "hello"), Outputs, "hello\n")
	app.waitFor(ct.JobEvents{"": {ct.JobStateUp: 1, ct.JobStateDown: 1}})

	detached := app.flynn("run", "-d", "echo", "world")
	t.Assert(detached, Succeeds)
	t.Assert(detached, c.Not(Outputs), "world\n")

	id := strings.TrimSpace(detached.Output)
	jobID := app.waitFor(ct.JobEvents{"": {ct.JobStateUp: 1, ct.JobStateDown: 1}})
	t.Assert(jobID, c.Equals, id)
	t.Assert(app.flynn("log", "--raw-output"), Outputs, "hello\nworld\n")

	// test stdin and stderr
	streams := app.flynnCmd("run", "sh", "-c", "cat 1>&2")
	stdin, err := streams.StdinPipe()
	t.Assert(err, c.IsNil)
	go func() {
		stdin.Write([]byte("goto stderr"))
		stdin.Close()
	}()
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	streams.Stderr = &stderr
	streams.Stdout = &stdout
	t.Assert(streams.Run(), c.IsNil)
	t.Assert(stderr.String(), c.Equals, "goto stderr")
	t.Assert(stdout.String(), c.Equals, "")

	// test exit code
	exit := app.sh("exit 42")
	t.Assert(exit, c.Not(Succeeds))
	if msg, ok := exit.Err.(*exec.ExitError); ok { // there is error code
		code := msg.Sys().(syscall.WaitStatus).ExitStatus()
		t.Assert(code, c.Equals, 42)
	} else {
		t.Fatal("There was no error code!")
	}
}
Example #8
0
func (s *GitDeploySuite) runBuildpackTestWithResponsePattern(t *c.C, name string, resources []string, pat string) {
	r := s.newGitRepo(t, "https://github.com/flynn-examples/"+name)

	t.Assert(r.flynn("create", name), Outputs, fmt.Sprintf("Created %s\n", name))

	for _, resource := range resources {
		t.Assert(r.flynn("resource", "add", resource), Succeeds)
	}

	watcher, err := s.controllerClient(t).WatchJobEvents(name, "")
	t.Assert(err, c.IsNil)
	defer watcher.Close()

	push := r.git("push", "flynn", "master")
	t.Assert(push, SuccessfulOutputContains, "Creating release")
	t.Assert(push, SuccessfulOutputContains, "Application deployed")
	t.Assert(push, SuccessfulOutputContains, "Waiting for web job to start...")
	t.Assert(push, SuccessfulOutputContains, "* [new branch]      master -> master")
	t.Assert(push, c.Not(OutputContains), "timed out waiting for scale")
	t.Assert(push, SuccessfulOutputContains, "=====> Default web formation scaled to 1")

	watcher.WaitFor(ct.JobEvents{"web": {ct.JobStateUp: 1}}, scaleTimeout, nil)

	route := name + ".dev"
	newRoute := r.flynn("route", "add", "http", route)
	t.Assert(newRoute, Succeeds)

	err = Attempts.Run(func() error {
		// Make HTTP requests
		client := &http.Client{}
		req, err := http.NewRequest("GET", "http://"+routerIP, nil)
		if err != nil {
			return err
		}
		req.Host = route
		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		contents, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return err
		}
		if res.StatusCode != 200 {
			return fmt.Errorf("Expected status 200, got %v", res.StatusCode)
		}
		m, err := regexp.MatchString(pat, string(contents))
		if err != nil {
			return err
		}
		if !m {
			return fmt.Errorf("Expected `%s`, got `%v`", pat, string(contents))
		}
		return nil
	})
	t.Assert(err, c.IsNil)

	t.Assert(r.flynn("scale", "web=0"), Succeeds)
}
Example #9
0
func (s *CLISuite) TestRoute(t *c.C) {
	app := s.newCliTestApp(t)
	defer app.cleanup()

	// The router API does not currently give us a "read your own writes"
	// guarantee, so we must retry a few times if we don't get the expected
	// result.
	assertRouteContains := func(str string, contained bool) {
		var res *CmdResult
		attempt.Strategy{
			Total: 10 * time.Second,
			Delay: 500 * time.Millisecond,
		}.Run(func() error {
			res = app.flynn("route")
			if contained == strings.Contains(res.Output, str) {
				return nil
			}
			return errors.New("unexpected output")
		})
		if contained {
			t.Assert(res, SuccessfulOutputContains, str)
		} else {
			t.Assert(res, c.Not(SuccessfulOutputContains), str)
		}
	}

	// flynn route add http
	route := random.String(32) + ".dev"
	newRoute := app.flynn("route", "add", "http", "--sticky", route)
	t.Assert(newRoute, Succeeds)
	routeID := strings.TrimSpace(newRoute.Output)
	assertRouteContains(routeID, true)

	// ensure sticky flag is set
	routes, err := s.controllerClient(t).RouteList(app.name)
	t.Assert(err, c.IsNil)
	var found bool
	for _, r := range routes {
		if fmt.Sprintf("%s/%s", r.Type, r.ID) != routeID {
			continue
		}
		t.Assert(r.Sticky, c.Equals, true)
		found = true
	}
	t.Assert(found, c.Equals, true, c.Commentf("didn't find route"))

	// flynn route remove
	t.Assert(app.flynn("route", "remove", routeID), Succeeds)
	assertRouteContains(routeID, false)

	// flynn route add tcp
	tcpRoute := app.flynn("route", "add", "tcp")
	t.Assert(tcpRoute, Succeeds)
	routeID = strings.Split(tcpRoute.Output, " ")[0]
	assertRouteContains(routeID, true)

	// flynn route remove
	t.Assert(app.flynn("route", "remove", routeID), Succeeds)
	assertRouteContains(routeID, false)
}
Example #10
0
func (h *Helper) newSlugrunnerArtifact(t *c.C) *ct.Artifact {
	r, err := h.controllerClient(t).GetAppRelease("gitreceive")
	t.Assert(err, c.IsNil)
	slugrunnerURI := r.Processes["app"].Env["SLUGRUNNER_IMAGE_URI"]
	t.Assert(slugrunnerURI, c.Not(c.Equals), "")
	return &ct.Artifact{Type: "docker", URI: slugrunnerURI}
}
Example #11
0
func (s *HostSuite) TestVolumeCreation(t *c.C) {
	h := s.anyHostClient(t)

	vol, err := h.CreateVolume("default")
	t.Assert(err, c.IsNil)
	t.Assert(vol.ID, c.Not(c.Equals), "")
	t.Assert(h.DestroyVolume(vol.ID), c.IsNil)
}
Example #12
0
func (s *CLISuite) TestScaleAll(t *c.C) {
	client := s.controllerClient(t)
	app := s.newCliTestApp(t)
	release := app.release
	defer app.cleanup()

	scale := app.flynn("scale", "echoer=1", "printer=2")
	t.Assert(scale, Succeeds)

	scale = app.flynn("scale", "--all")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, fmt.Sprintf("%s (current)\n", release.ID))
	t.Assert(scale, SuccessfulOutputContains, "echoer=1")
	t.Assert(scale, SuccessfulOutputContains, "printer=2")

	prevRelease := release
	release = &ct.Release{
		ArtifactID: release.ArtifactID,
		Env:        release.Env,
		Meta:       release.Meta,
		Processes:  release.Processes,
	}
	t.Assert(client.CreateRelease(release), c.IsNil)
	t.Assert(client.SetAppRelease(app.id, release.ID), c.IsNil)

	scale = app.flynn("scale", "echoer=2", "printer=1")
	t.Assert(scale, Succeeds)

	scale = app.flynn("scale", "--all")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, fmt.Sprintf("%s (current)\n", release.ID))
	t.Assert(scale, SuccessfulOutputContains, "echoer=2")
	t.Assert(scale, SuccessfulOutputContains, "printer=1")
	t.Assert(scale, SuccessfulOutputContains, fmt.Sprintf("%s\n", prevRelease.ID))
	t.Assert(scale, SuccessfulOutputContains, "echoer=1")
	t.Assert(scale, SuccessfulOutputContains, "printer=2")

	scale = app.flynn("scale", "--all", "--release", release.ID)
	t.Assert(scale, c.Not(Succeeds))

	scale = app.flynn("scale", "--all", "echoer=3", "printer=3")
	t.Assert(scale, c.Not(Succeeds))
}
Example #13
0
// This test emulates deploys in the dashboard app
func (s *TaffyDeploySuite) TestDeploys(t *c.C) {
	client := s.controllerClient(t)

	github := map[string]string{
		"user_login": "******",
		"repo_name":  "go-flynn-example",
		"ref":        "master",
		"sha":        "a2ac6b059e1359d0e974636935fda8995de02b16",
		"clone_url":  "https://github.com/flynn-examples/go-flynn-example.git",
	}

	// initial deploy

	app := &ct.App{
		Meta: map[string]string{
			"type":       "github",
			"user_login": github["user_login"],
			"repo_name":  github["repo_name"],
			"ref":        github["ref"],
			"sha":        github["sha"],
			"clone_url":  github["clone_url"],
		},
	}
	t.Assert(client.CreateApp(app), c.IsNil)
	debugf(t, "created app %s (%s)", app.Name, app.ID)

	s.deployWithTaffy(t, app, github)

	_, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	// second deploy

	github["sha"] = "2bc7e016b1b4aae89396c898583763c5781e031a"

	release, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	release = &ct.Release{
		Env:       release.Env,
		Processes: release.Processes,
	}
	t.Assert(client.CreateRelease(release), c.IsNil)
	t.Assert(client.SetAppRelease(app.ID, release.ID), c.IsNil)

	s.deployWithTaffy(t, app, github)

	newRelease, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)
	t.Assert(newRelease.ID, c.Not(c.Equals), release.ID)
	release.Env["SLUG_URL"] = newRelease.Env["SLUG_URL"] // SLUG_URL will be different
	t.Assert(release.Env, c.DeepEquals, newRelease.Env)
	t.Assert(release.Processes, c.DeepEquals, newRelease.Processes)
}
Example #14
0
func (s *CLISuite) TestResourceRemove(t *c.C) {
	app := s.newCliTestApp(t)
	defer app.cleanup()

	add := app.flynn("resource", "add", "postgres")
	t.Assert(add, Succeeds)
	t.Assert(app.flynn("resource").Output, Matches, "postgres")
	t.Assert(app.flynn("env").Output, Matches, "FLYNN_POSTGRES")
	id := strings.Split(add.Output, " ")[2]

	// change one of the env vars provided by the resource
	t.Assert(app.flynn("env", "set", "PGUSER=testuser"), Succeeds)

	remove := app.flynn("resource", "remove", "postgres", id)
	t.Assert(remove, Succeeds)

	t.Assert(app.flynn("resource").Output, c.Not(Matches), "postgres")
	// test that unmodified vars are removed
	t.Assert(app.flynn("env").Output, c.Not(Matches), "FLYNN_POSTGRES")
	// but that modifed ones are retained
	t.Assert(app.flynn("env", "get", "PGUSER").Output, Matches, "testuser")
}
Example #15
0
func (s *CLISuite) TestReleaseDelete(t *c.C) {
	// create an app and release it twice
	r := s.newGitRepo(t, "http")
	app := "release-delete-" + random.String(8)
	t.Assert(r.flynn("create", app), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)
	t.Assert(r.git("commit", "--allow-empty", "--message", "empty commit"), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)

	// get the releases
	client := s.controllerClient(t)
	releases, err := client.AppReleaseList(app)
	t.Assert(err, c.IsNil)
	t.Assert(releases, c.HasLen, 2)

	// check the current release cannot be deleted
	res := r.flynn("release", "delete", "--yes", releases[0].ID)
	t.Assert(res, c.Not(Succeeds))
	t.Assert(res.Output, c.Equals, "validation_error: cannot delete current app release\n")

	// associate the initial release with another app
	otherApp := &ct.App{Name: "release-delete-" + random.String(8)}
	t.Assert(client.CreateApp(otherApp), c.IsNil)
	t.Assert(client.PutFormation(&ct.Formation{AppID: otherApp.ID, ReleaseID: releases[1].ID}), c.IsNil)

	// check deleting the initial release just deletes the formation
	res = r.flynn("release", "delete", "--yes", releases[1].ID)
	t.Assert(res, Succeeds)
	t.Assert(res.Output, c.Equals, "Release scaled down for app but not fully deleted (still associated with 1 other apps)\n")

	// check the slug artifact still exists
	slugArtifact, err := client.GetArtifact(releases[1].FileArtifactIDs()[0])
	t.Assert(err, c.IsNil)
	s.assertURI(t, slugArtifact.URI, http.StatusOK)

	// check the inital release can now be deleted
	res = r.flynn("-a", otherApp.ID, "release", "delete", "--yes", releases[1].ID)
	t.Assert(res, Succeeds)
	t.Assert(res.Output, c.Equals, fmt.Sprintf("Deleted release %s (deleted 1 files)\n", releases[1].ID))

	// check the slug artifact was deleted
	_, err = client.GetArtifact(slugArtifact.ID)
	t.Assert(err, c.Equals, controller.ErrNotFound)
	s.assertURI(t, slugArtifact.URI, http.StatusNotFound)

	// check the image artifact was not deleted (since it is shared between both releases)
	_, err = client.GetArtifact(releases[1].ImageArtifactID())
	t.Assert(err, c.IsNil)
}
Example #16
0
func (s *ControllerSuite) TestBackup(t *c.C) {
	client := s.controllerClient(t)
	out, err := client.Backup()
	t.Assert(err, c.IsNil)
	defer out.Close()
	data := make(map[string][]byte)
	tr := tar.NewReader(out)
	for {
		h, err := tr.Next()
		if err == io.EOF {
			break
		}
		t.Assert(err, c.IsNil)
		b := make([]byte, h.Size)
		_, err = tr.Read(b)
		t.Assert(err, c.IsNil)
		_, filename := filepath.Split(h.Name)
		data[filename] = b
	}
	sql, ok := data["postgres.sql.gz"]
	t.Assert(ok, c.Equals, true)
	t.Assert(len(sql) > 0, c.Equals, true)
	flynn, ok := data["flynn.json"]
	t.Assert(ok, c.Equals, true)
	var apps map[string]*ct.ExpandedFormation
	t.Assert(json.Unmarshal(flynn, &apps), c.IsNil)
	for _, name := range []string{"postgres", "discoverd", "flannel", "controller"} {
		ef, ok := apps[name]
		t.Assert(ok, c.Equals, true)
		t.Assert(ef.App, c.Not(c.IsNil))
		t.Assert(ef.Release, c.Not(c.IsNil))
		t.Assert(ef.Artifact, c.Not(c.IsNil))
		t.Assert(ef.Processes, c.Not(c.IsNil))
		t.Assert(ef.App.Name, c.Equals, name)
	}
}
Example #17
0
func (s *CLISuite) TestRunNoImage(t *c.C) {
	r := s.newGitRepo(t, "empty-release")
	t.Assert(r.flynn("create"), Succeeds)
	t.Assert(r.flynn("env", "set", "FOO=BAR", "BUILDPACK_URL=https://github.com/kr/heroku-buildpack-inline"), Succeeds)

	// running a command before pushing should error
	cmd := r.flynn("run", "env")
	t.Assert(cmd, c.Not(Succeeds))
	t.Assert(cmd, OutputContains, "App release has no image, push a release first")

	// command should work after push
	t.Assert(r.git("push", "flynn", "master"), Succeeds)
	cmd = r.flynn("run", "env")
	t.Assert(cmd, Succeeds)
	t.Assert(cmd, OutputContains, "FOO=BAR")
}
Example #18
0
func (s *HostSuite) TestAddFailingJob(t *c.C) {
	// get a host and watch events
	hosts, err := s.clusterClient(t).Hosts()
	t.Assert(err, c.IsNil)
	t.Assert(hosts, c.Not(c.HasLen), 0)
	h := hosts[0]
	jobID := random.UUID()
	events := make(chan *host.Event)
	stream, err := h.StreamEvents(jobID, events)
	t.Assert(err, c.IsNil)
	defer stream.Close()

	// add a job with a non existent partition
	job := &host.Job{
		ID: jobID,
		ImageArtifact: &host.Artifact{
			Type: host.ArtifactTypeDocker,
			URI:  "http://example.com?name=foo&id=bar",
		},
		Partition: "nonexistent",
	}
	t.Assert(h.AddJob(job), c.IsNil)

	// check we get a create then error event
	actual := make([]*host.Event, 0, 2)
loop:
	for {
		select {
		case e, ok := <-events:
			if !ok {
				t.Fatalf("job event stream closed unexpectedly: %s", stream.Err())
			}
			actual = append(actual, e)
			if len(actual) >= 2 {
				break loop
			}
		case <-time.After(30 * time.Second):
			t.Fatal("timed out waiting for job event")
		}
	}
	t.Assert(actual, c.HasLen, 2)
	t.Assert(actual[0].Event, c.Equals, host.JobEventCreate)
	t.Assert(actual[1].Event, c.Equals, host.JobEventError)
	jobErr := actual[1].Job.Error
	t.Assert(jobErr, c.NotNil)
	t.Assert(*jobErr, c.Equals, `host: invalid job partition "nonexistent"`)
}
Example #19
0
func (s *GitDeploySuite) TestBuildCaching(t *c.C) {
	r := s.newGitRepo(t, "build-cache")
	t.Assert(r.flynn("create"), Succeeds)
	t.Assert(r.flynn("env", "set", "BUILDPACK_URL=https://github.com/kr/heroku-buildpack-inline"), Succeeds)

	r.git("commit", "-m", "bump", "--allow-empty")
	push := r.git("push", "flynn", "master")
	t.Assert(push, Succeeds)
	t.Assert(push, c.Not(OutputContains), "cached")

	r.git("commit", "-m", "bump", "--allow-empty")
	push = r.git("push", "flynn", "master")
	t.Assert(push, SuccessfulOutputContains, "cached: 0")

	r.git("commit", "-m", "bump", "--allow-empty")
	push = r.git("push", "flynn", "master")
	t.Assert(push, SuccessfulOutputContains, "cached: 1")
}
Example #20
0
func (s *RedisSuite) TestDumpRestore(t *c.C) {
	a := s.newCliTestApp(t)
	t.Assert(a.flynn("resource", "add", "redis"), Succeeds)

	release, err := s.controllerClient(t).GetAppRelease(a.id)
	t.Assert(err, c.IsNil)

	t.Assert(release.Env["FLYNN_REDIS"], c.Not(c.Equals), "")
	a.waitForService(release.Env["FLYNN_REDIS"])

	t.Assert(a.flynn("redis", "redis-cli", "set", "foo", "bar"), Succeeds)

	file := filepath.Join(t.MkDir(), "dump.rdb")
	t.Assert(a.flynn("redis", "dump", "-f", file), Succeeds)
	t.Assert(a.flynn("redis", "redis-cli", "del", "foo"), Succeeds)

	a.flynn("redis", "restore", "-f", file)

	query := a.flynn("redis", "redis-cli", "get", "foo")
	t.Assert(query, SuccessfulOutputContains, "bar")
}
Example #21
0
func (s *GitreceiveSuite) TestRepoCaching(t *c.C) {
	r := s.newGitRepo(t, "empty")
	t.Assert(r.flynn("create"), Succeeds)

	r.git("commit", "-m", "bump", "--allow-empty")
	r.git("commit", "-m", "bump", "--allow-empty")
	push := r.git("push", "flynn", "master")
	t.Assert(push, Succeeds)
	t.Assert(push, c.Not(OutputContains), "cached")

	// cycle the receiver to clear any cache
	t.Assert(flynn(t, "/", "-a", "gitreceive", "scale", "app=0"), Succeeds)
	t.Assert(flynn(t, "/", "-a", "gitreceive", "scale", "app=1"), Succeeds)
	_, err := s.discoverdClient(t).Instances("gitreceive", 10*time.Second)
	t.Assert(err, c.IsNil)

	r.git("commit", "-m", "bump", "--allow-empty")
	push = r.git("push", "flynn", "master", "--progress")
	// should only contain one object
	t.Assert(push, SuccessfulOutputContains, "Counting objects: 1, done.")
}
Example #22
0
func (s *CLISuite) TestRoute(t *c.C) {
	client := s.controllerClient(t)
	app := s.newCliTestApp(t)
	defer app.cleanup()

	// The router API does not currently give us a "read your own writes"
	// guarantee, so we must retry a few times if we don't get the expected
	// result.
	assertRouteContains := func(str string, contained bool) {
		var res *CmdResult
		attempt.Strategy{
			Total: 10 * time.Second,
			Delay: 500 * time.Millisecond,
		}.Run(func() error {
			res = app.flynn("route")
			if contained == strings.Contains(res.Output, str) {
				return nil
			}
			return errors.New("unexpected output")
		})
		if contained {
			t.Assert(res, SuccessfulOutputContains, str)
		} else {
			t.Assert(res, c.Not(SuccessfulOutputContains), str)
		}
	}

	// flynn route add http
	route := random.String(32) + ".dev"
	newRoute := app.flynn("route", "add", "http", "--sticky", route)
	t.Assert(newRoute, Succeeds)
	routeID := strings.TrimSpace(newRoute.Output)
	assertRouteContains(routeID, true)

	// duplicate http route
	dupRoute := app.flynn("route", "add", "http", "--sticky", route)
	t.Assert(dupRoute, c.Not(Succeeds))
	t.Assert(dupRoute.Output, c.Equals, "conflict: Duplicate route\n")

	// ensure sticky flag is set
	routes, err := client.RouteList(app.name)
	t.Assert(err, c.IsNil)
	var found bool
	for _, r := range routes {
		if fmt.Sprintf("%s/%s", r.Type, r.ID) != routeID {
			continue
		}
		t.Assert(r.Sticky, c.Equals, true)
		found = true
	}
	t.Assert(found, c.Equals, true, c.Commentf("didn't find route"))

	// flynn route update --no-sticky
	newRoute = app.flynn("route", "update", routeID, "--no-sticky")
	t.Assert(newRoute, Succeeds)
	r, err := client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Sticky, c.Equals, false)

	// flynn route update --service
	newRoute = app.flynn("route", "update", routeID, "--service", "foo")
	t.Assert(newRoute, Succeeds)
	r, err = client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Service, c.Equals, "foo")
	t.Assert(r.Sticky, c.Equals, false)

	// flynn route update --sticky
	newRoute = app.flynn("route", "update", routeID, "--sticky")
	t.Assert(newRoute, Succeeds)
	r, err = client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Sticky, c.Equals, true)
	t.Assert(r.Service, c.Equals, "foo")

	// flynn route add domain path
	pathRoute := app.flynn("route", "add", "http", route+"/path/")
	t.Assert(pathRoute, Succeeds)
	pathRouteID := strings.TrimSpace(pathRoute.Output)
	assertRouteContains(pathRouteID, true)

	// flynn route add domain path duplicate
	dupRoute = app.flynn("route", "add", "http", route+"/path/")
	t.Assert(dupRoute, c.Not(Succeeds))
	t.Assert(dupRoute.Output, c.Equals, "conflict: Duplicate route\n")

	// flynn route add domain path without trailing should correct to trailing
	noTrailingRoute := app.flynn("route", "add", "http", route+"/path2")
	t.Assert(noTrailingRoute, Succeeds)
	noTrailingRouteID := strings.TrimSpace(noTrailingRoute.Output)
	assertRouteContains(noTrailingRouteID, true)
	// flynn route should show the corrected trailing path
	assertRouteContains("/path2/", true)

	// flynn route remove should fail because of dependent route
	delFail := app.flynn("route", "remove", routeID)
	t.Assert(delFail, c.Not(Succeeds))

	// But removing the dependent route and then the default route should work
	t.Assert(app.flynn("route", "remove", pathRouteID), Succeeds)
	assertRouteContains(pathRouteID, false)
	t.Assert(app.flynn("route", "remove", noTrailingRouteID), Succeeds)
	assertRouteContains(noTrailingRouteID, false)
	t.Assert(app.flynn("route", "remove", routeID), Succeeds)
	assertRouteContains(routeID, false)

	// flynn route add tcp
	tcpRoute := app.flynn("route", "add", "tcp")
	t.Assert(tcpRoute, Succeeds)
	routeID = strings.Split(tcpRoute.Output, " ")[0]
	assertRouteContains(routeID, true)

	// flynn route add tcp --port
	portRoute := app.flynn("route", "add", "tcp", "--port", "9999")
	t.Assert(portRoute, Succeeds)
	routeID = strings.Split(portRoute.Output, " ")[0]
	port := strings.Split(portRoute.Output, " ")[4]
	t.Assert(port, c.Equals, "9999\n")
	assertRouteContains(routeID, true)

	// flynn route update --service
	portRoute = app.flynn("route", "update", routeID, "--service", "foo")
	t.Assert(portRoute, Succeeds)
	r, err = client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Service, c.Equals, "foo")

	// flynn route remove
	t.Assert(app.flynn("route", "remove", routeID), Succeeds)
	assertRouteContains(routeID, false)

	writeTemp := func(data, prefix string) (string, error) {
		f, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("flynn-test-%s", prefix))
		t.Assert(err, c.IsNil)
		_, err = f.WriteString(data)
		t.Assert(err, c.IsNil)
		stat, err := f.Stat()
		t.Assert(err, c.IsNil)
		return filepath.Join(os.TempDir(), stat.Name()), nil
	}

	// flynn route add http with tls cert
	cert, err := tlscert.Generate([]string{"example.com"})
	t.Assert(err, c.IsNil)
	certPath, err := writeTemp(cert.Cert, "tls-cert")
	t.Assert(err, c.IsNil)
	keyPath, err := writeTemp(cert.PrivateKey, "tls-key")
	certRoute := app.flynn("route", "add", "http", "--tls-cert", certPath, "--tls-key", keyPath, "example.com")
	t.Assert(certRoute, Succeeds)
	routeID = strings.TrimSpace(certRoute.Output)
	r, err = client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Domain, c.Equals, "example.com")
	t.Assert(r.TLSCert, c.Equals, cert.Cert)
	t.Assert(r.TLSKey, c.Equals, cert.PrivateKey)

	// flynn route update tls cert
	cert, err = tlscert.Generate([]string{"example.com"})
	t.Assert(err, c.IsNil)
	certPath, err = writeTemp(cert.Cert, "tls-cert")
	t.Assert(err, c.IsNil)
	keyPath, err = writeTemp(cert.PrivateKey, "tls-key")
	certRoute = app.flynn("route", "update", routeID, "--tls-cert", certPath, "--tls-key", keyPath)
	t.Assert(certRoute, Succeeds)
	r, err = client.GetRoute(app.id, routeID)
	t.Assert(err, c.IsNil)
	t.Assert(r.Domain, c.Equals, "example.com")
	t.Assert(r.TLSCert, c.Equals, cert.Cert)
	t.Assert(r.TLSKey, c.Equals, cert.PrivateKey)

	// flynn route remove
	t.Assert(app.flynn("route", "remove", routeID), Succeeds)
	assertRouteContains(routeID, false)
}
Example #23
0
func (s *DomainMigrationSuite) migrateDomain(t *c.C, dm *ct.DomainMigration) {
	debugf(t, "migrating domain from %s to %s", dm.OldDomain, dm.Domain)
	client := s.controllerClient(t)

	events := make(chan *ct.Event)
	stream, err := client.StreamEvents(controller.StreamEventsOptions{
		ObjectTypes: []ct.EventType{ct.EventTypeDomainMigration},
	}, events)
	t.Assert(err, c.IsNil)
	defer stream.Close()

	prevRouterRelease, err := client.GetAppRelease("router")
	t.Assert(err, c.IsNil)

	err = client.PutDomain(dm)
	t.Assert(err, c.IsNil)

	waitEvent := func(typ string, timeout time.Duration) (event ct.DomainMigrationEvent) {
		debugf(t, "waiting for %s domain migration event", typ)
		var e *ct.Event
		var ok bool
		select {
		case e, ok = <-events:
			if !ok {
				t.Fatal("event stream closed unexpectedly")
			}
			debugf(t, "got %s domain migration event", typ)
		case <-time.After(timeout):
			t.Fatalf("timed out waiting for %s domain migration event", typ)
		}
		t.Assert(e.Data, c.NotNil)
		t.Assert(json.Unmarshal(e.Data, &event), c.IsNil)
		return
	}

	// created
	event := waitEvent("initial", 2*time.Minute)
	t.Assert(event.Error, c.Equals, "")
	t.Assert(event.DomainMigration, c.NotNil)
	t.Assert(event.DomainMigration.ID, c.Equals, dm.ID)
	t.Assert(event.DomainMigration.OldDomain, c.Equals, dm.OldDomain)
	t.Assert(event.DomainMigration.Domain, c.Equals, dm.Domain)
	t.Assert(event.DomainMigration.TLSCert, c.IsNil)
	t.Assert(event.DomainMigration.OldTLSCert, c.NotNil)
	t.Assert(event.DomainMigration.CreatedAt, c.NotNil)
	t.Assert(event.DomainMigration.CreatedAt.Equal(*dm.CreatedAt), c.Equals, true)
	t.Assert(event.DomainMigration.FinishedAt, c.IsNil)

	// complete
	event = waitEvent("final", 3*time.Minute)
	t.Assert(event.Error, c.Equals, "")
	t.Assert(event.DomainMigration, c.NotNil)
	t.Assert(event.DomainMigration.ID, c.Equals, dm.ID)
	t.Assert(event.DomainMigration.OldDomain, c.Equals, dm.OldDomain)
	t.Assert(event.DomainMigration.Domain, c.Equals, dm.Domain)
	t.Assert(event.DomainMigration.TLSCert, c.NotNil)
	t.Assert(event.DomainMigration.OldTLSCert, c.NotNil)
	t.Assert(event.DomainMigration.CreatedAt, c.NotNil)
	t.Assert(event.DomainMigration.CreatedAt.Equal(*dm.CreatedAt), c.Equals, true)
	t.Assert(event.DomainMigration.FinishedAt, c.NotNil)

	cert := event.DomainMigration.TLSCert

	controllerRelease, err := client.GetAppRelease("controller")
	t.Assert(err, c.IsNil)
	t.Assert(controllerRelease.Env["DEFAULT_ROUTE_DOMAIN"], c.Equals, dm.Domain)
	t.Assert(controllerRelease.Env["CA_CERT"], c.Equals, cert.CACert)

	routerRelease, err := client.GetAppRelease("router")
	t.Assert(err, c.IsNil)
	t.Assert(routerRelease.Env["TLSCERT"], c.Equals, cert.Cert)
	t.Assert(routerRelease.Env["TLSKEY"], c.Not(c.Equals), "")
	t.Assert(routerRelease.Env["TLSKEY"], c.Not(c.Equals), prevRouterRelease.Env["TLSKEY"])

	dashboardRelease, err := client.GetAppRelease("dashboard")
	t.Assert(err, c.IsNil)
	t.Assert(dashboardRelease.Env["DEFAULT_ROUTE_DOMAIN"], c.Equals, dm.Domain)
	t.Assert(dashboardRelease.Env["CONTROLLER_DOMAIN"], c.Equals, fmt.Sprintf("controller.%s", dm.Domain))
	t.Assert(dashboardRelease.Env["URL"], c.Equals, fmt.Sprintf("dashboard.%s", dm.Domain))
	t.Assert(dashboardRelease.Env["CA_CERT"], c.Equals, cert.CACert)

	var doPing func(string, int)
	doPing = func(component string, retriesRemaining int) {
		url := fmt.Sprintf("http://%s.%s/ping", component, dm.Domain)
		res, err := (&http.Client{}).Get(url)
		if (err != nil || res.StatusCode != 200) && retriesRemaining > 0 {
			time.Sleep(100 * time.Millisecond)
			doPing(component, retriesRemaining-1)
			return
		}
		t.Assert(err, c.IsNil)
		t.Assert(res.StatusCode, c.Equals, 200, c.Commentf("failed to ping %s", component))
	}
	doPing("controller", 3)
	doPing("dashboard", 3)
}
Example #24
0
// This test emulates deploys in the dashboard app
func (s *TaffyDeploySuite) TestDeploys(t *c.C) {
	assertMeta := func(m map[string]string, k string, checker c.Checker, args ...interface{}) {
		v, ok := m[k]
		t.Assert(ok, c.Equals, true)
		t.Assert(v, checker, args...)
	}

	client := s.controllerClient(t)

	github := map[string]string{
		"user":      "******",
		"repo":      "go-flynn-example",
		"branch":    "master",
		"rev":       "a2ac6b059e1359d0e974636935fda8995de02b16",
		"clone_url": "https://github.com/flynn-examples/go-flynn-example.git",
	}

	// initial deploy

	app := &ct.App{}
	t.Assert(client.CreateApp(app), c.IsNil)
	debugf(t, "created app %s (%s)", app.Name, app.ID)

	env := map[string]string{
		"SOMEVAR": "SOMEVAL",
	}
	meta := map[string]string{
		"github":      "true",
		"github_user": github["user"],
		"github_repo": github["repo"],
	}
	s.deployWithTaffy(t, app, env, meta, github)

	release, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)
	t.Assert(release, c.NotNil)
	t.Assert(release.Meta, c.NotNil)
	assertMeta(release.Meta, "git", c.Equals, "true")
	assertMeta(release.Meta, "clone_url", c.Equals, github["clone_url"])
	assertMeta(release.Meta, "branch", c.Equals, github["branch"])
	assertMeta(release.Meta, "rev", c.Equals, github["rev"])
	assertMeta(release.Meta, "taffy_job", c.Not(c.Equals), "")
	assertMeta(release.Meta, "github", c.Equals, "true")
	assertMeta(release.Meta, "github_user", c.Equals, github["user"])
	assertMeta(release.Meta, "github_repo", c.Equals, github["repo"])
	t.Assert(release.Env, c.NotNil)
	assertMeta(release.Env, "SOMEVAR", c.Equals, "SOMEVAL")

	// second deploy

	github["rev"] = "2bc7e016b1b4aae89396c898583763c5781e031a"

	release, err = client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	s.deployWithTaffy(t, app, env, meta, github)

	newRelease, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)
	t.Assert(newRelease.ID, c.Not(c.Equals), release.ID)
	env["SLUG_URL"] = newRelease.Env["SLUG_URL"] // SLUG_URL will be different
	t.Assert(env, c.DeepEquals, newRelease.Env)
	t.Assert(release.Processes, c.DeepEquals, newRelease.Processes)
	t.Assert(newRelease, c.NotNil)
	t.Assert(newRelease.Meta, c.NotNil)
	assertMeta(newRelease.Meta, "git", c.Equals, "true")
	assertMeta(newRelease.Meta, "clone_url", c.Equals, github["clone_url"])
	assertMeta(newRelease.Meta, "branch", c.Equals, github["branch"])
	assertMeta(newRelease.Meta, "rev", c.Equals, github["rev"])
	assertMeta(newRelease.Meta, "taffy_job", c.Not(c.Equals), "")
	assertMeta(newRelease.Meta, "github", c.Equals, "true")
	assertMeta(newRelease.Meta, "github_user", c.Equals, github["user"])
	assertMeta(newRelease.Meta, "github_repo", c.Equals, github["repo"])
}
Example #25
0
func (s *CLISuite) TestScale(t *c.C) {
	app := s.newCliTestApp(t)

	assertEventOutput := func(scale *CmdResult, events ct.JobEvents) {
		var actual []*ct.JobEvent
		f := func(e *ct.JobEvent) error {
			actual = append(actual, e)
			return nil
		}
		t.Assert(app.watcher.WaitFor(events, scaleTimeout, f), c.IsNil)
		for _, e := range actual {
			t.Assert(scale, OutputContains, fmt.Sprintf("==> %s %s %s", e.Type, e.JobID, e.State))
		}
	}

	scale := app.flynn("scale", "echoer=1")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "scaling echoer: 0=>1")
	t.Assert(scale, SuccessfulOutputContains, "scale completed")
	assertEventOutput(scale, ct.JobEvents{"echoer": {"up": 1}})

	scale = app.flynn("scale", "echoer=3", "printer=1")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "echoer: 1=>3")
	t.Assert(scale, SuccessfulOutputContains, "printer: 0=>1")
	t.Assert(scale, SuccessfulOutputContains, "scale completed")
	assertEventOutput(scale, ct.JobEvents{"echoer": {"up": 2}, "printer": {"up": 1}})

	// no args should show current scale
	scale = app.flynn("scale")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "echoer=3")
	t.Assert(scale, SuccessfulOutputContains, "printer=1")
	t.Assert(scale, SuccessfulOutputContains, "crasher=0")
	t.Assert(scale, SuccessfulOutputContains, "omni=0")

	// scale should only affect specified processes
	scale = app.flynn("scale", "printer=2")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "printer: 1=>2")
	t.Assert(scale, SuccessfulOutputContains, "scale completed")
	assertEventOutput(scale, ct.JobEvents{"printer": {"up": 1}})
	scale = app.flynn("scale")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "echoer=3")
	t.Assert(scale, SuccessfulOutputContains, "printer=2")
	t.Assert(scale, SuccessfulOutputContains, "crasher=0")
	t.Assert(scale, SuccessfulOutputContains, "omni=0")

	// unchanged processes shouldn't appear in output
	scale = app.flynn("scale", "echoer=3", "printer=0")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "printer: 2=>0")
	t.Assert(scale, c.Not(OutputContains), "echoer")
	t.Assert(scale, SuccessfulOutputContains, "scale completed")
	assertEventOutput(scale, ct.JobEvents{"printer": {"down": 2}})

	// --no-wait should not wait for scaling to complete
	scale = app.flynn("scale", "--no-wait", "echoer=0")
	t.Assert(scale, Succeeds)
	t.Assert(scale, SuccessfulOutputContains, "scaling echoer: 3=>0")
	t.Assert(scale, c.Not(OutputContains), "scale completed")
}
Example #26
0
func (s *SchedulerSuite) TestControllerRestart(t *c.C) {
	// get the current controller details
	app, err := s.controllerClient(t).GetApp("controller")
	t.Assert(err, c.IsNil)
	release, err := s.controllerClient(t).GetAppRelease("controller")
	t.Assert(err, c.IsNil)
	list, err := s.controllerClient(t).JobList("controller")
	t.Assert(err, c.IsNil)
	var jobs []*ct.Job
	for _, job := range list {
		if job.Type == "web" {
			jobs = append(jobs, job)
		}
	}
	t.Assert(jobs, c.HasLen, 1)
	hostID, jobID, _ := cluster.ParseJobID(jobs[0].ID)
	t.Assert(hostID, c.Not(c.Equals), "")
	t.Assert(jobID, c.Not(c.Equals), "")
	debugf(t, "current controller app[%s] host[%s] job[%s]", app.ID, hostID, jobID)

	// start a second controller and wait for it to come up
	stream, err := s.controllerClient(t).StreamJobEvents("controller", 0)
	t.Assert(err, c.IsNil)
	debug(t, "scaling the controller up")
	t.Assert(s.controllerClient(t).PutFormation(&ct.Formation{
		AppID:     app.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"web": 2, "scheduler": 1},
	}), c.IsNil)
	lastID, _ := waitForJobEvents(t, stream.Events, jobEvents{"web": {"up": 1}})
	stream.Close()

	// get direct client for new controller
	var client *controller.Client
	attempts := attempt.Strategy{
		Total: 10 * time.Second,
		Delay: 500 * time.Millisecond,
	}
	t.Assert(attempts.Run(func() (err error) {
		set, err := s.discoverdClient(t).NewServiceSet("flynn-controller")
		if err != nil {
			return err
		}
		defer set.Close()
		addrs := set.Addrs()
		if len(addrs) != 2 {
			return fmt.Errorf("expected 2 controller processes, got %d", len(addrs))
		}
		addr := addrs[1]
		debug(t, "new controller address: ", addr)
		client, err = controller.NewClient("http://"+addr, s.clusterConf(t).Key)
		return
	}), c.IsNil)

	// kill the first controller and check the scheduler brings it back online
	stream, err = client.StreamJobEvents("controller", lastID)
	defer stream.Close()
	t.Assert(err, c.IsNil)
	cc, err := cluster.NewClientWithDial(nil, s.discoverdClient(t).NewServiceSet)
	t.Assert(err, c.IsNil)
	defer cc.Close()
	hc, err := cc.DialHost(hostID)
	t.Assert(err, c.IsNil)
	defer hc.Close()
	debug(t, "stopping job ", jobID)
	t.Assert(hc.StopJob(jobID), c.IsNil)
	waitForJobEvents(t, stream.Events, jobEvents{"web": {"down": 1, "up": 1}})

	// scale back down
	debug(t, "scaling the controller down")
	t.Assert(s.controllerClient(t).PutFormation(&ct.Formation{
		AppID:     app.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"web": 1, "scheduler": 1},
	}), c.IsNil)
	waitForJobEvents(t, stream.Events, jobEvents{"web": {"down": 1}})

	// unset the suite's client so other tests use a new client
	s.controller = nil
}
Example #27
0
func (s *SchedulerSuite) TestDeployController(t *c.C) {
	// get the current controller release
	client := s.controllerClient(t)
	app, err := client.GetApp("controller")
	t.Assert(err, c.IsNil)
	release, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	// get the current controller formation
	formation, err := client.GetFormation(app.ID, release.ID)
	t.Assert(err, c.IsNil)

	// create a controller deployment
	release.ID = ""
	t.Assert(client.CreateRelease(release), c.IsNil)
	deployment, err := client.CreateDeployment(app.ID, release.ID)
	t.Assert(err, c.IsNil)

	events := make(chan *ct.DeploymentEvent)
	eventStream, err := client.StreamDeployment(deployment, events)
	t.Assert(err, c.IsNil)
	defer eventStream.Close()

	// wait for the deploy to complete (this doesn't wait for specific events
	// due to the fact that when the deployer deploys itself, some events will
	// not get sent)
loop:
	for {
		select {
		case e, ok := <-events:
			if !ok {
				t.Fatal("unexpected close of deployment event stream")
			}
			debugf(t, "got deployment event: %s %s", e.JobType, e.JobState)
			switch e.Status {
			case "complete":
				break loop
			case "failed":
				t.Fatal("the deployment failed")
			}
		case <-time.After(time.Duration(app.DeployTimeout) * time.Second):
			t.Fatal("timed out waiting for the deploy to complete")
		}
	}

	// check the correct controller jobs are running
	hosts, err := s.clusterClient(t).Hosts()
	t.Assert(err, c.IsNil)
	t.Assert(hosts, c.Not(c.HasLen), 0)
	actual := make(map[string]map[string]int)
	for _, h := range hosts {
		jobs, err := h.ListJobs()
		t.Assert(err, c.IsNil)
		for _, job := range jobs {
			if job.Status != host.StatusRunning {
				continue
			}
			appID := job.Job.Metadata["flynn-controller.app"]
			if appID != app.ID {
				continue
			}
			releaseID := job.Job.Metadata["flynn-controller.release"]
			if _, ok := actual[releaseID]; !ok {
				actual[releaseID] = make(map[string]int)
			}
			typ := job.Job.Metadata["flynn-controller.type"]
			actual[releaseID][typ]++
		}
	}
	expected := map[string]map[string]int{release.ID: {
		"web":       formation.Processes["web"],
		"worker":    formation.Processes["worker"],
		"scheduler": len(hosts),
	}}
	t.Assert(actual, c.DeepEquals, expected)
}
Example #28
0
func (s *SchedulerSuite) TestRollbackController(t *c.C) {
	// get the current controller release
	client := s.controllerClient(t)
	app, err := client.GetApp("controller")
	t.Assert(err, c.IsNil)
	release, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	watcher, err := s.controllerClient(t).WatchJobEvents(app.ID, release.ID)
	t.Assert(err, c.IsNil)
	defer watcher.Close()

	// get the current controller formation
	formation, err := client.GetFormation(app.ID, release.ID)
	t.Assert(err, c.IsNil)

	currentReleaseID := release.ID

	// create a controller deployment that will fail
	release.ID = ""
	worker := release.Processes["worker"]
	worker.Entrypoint = []string{"/i/dont/exist"}
	release.Processes["worker"] = worker
	t.Assert(client.CreateRelease(release), c.IsNil)
	deployment, err := client.CreateDeployment(app.ID, release.ID)
	t.Assert(err, c.IsNil)

	events := make(chan *ct.DeploymentEvent)
	eventStream, err := client.StreamDeployment(deployment, events)
	t.Assert(err, c.IsNil)
	defer eventStream.Close()

	// wait for the deploy to fail
loop:
	for {
		select {
		case e, ok := <-events:
			if !ok {
				t.Fatal("unexpected close of deployment event stream")
			}
			debugf(t, "got deployment event: %s %s", e.JobType, e.JobState)
			switch e.Status {
			case "complete":
				t.Fatal("the deployment succeeded when it should have failed")
			case "failed":
				break loop
			}
		case <-time.After(2 * time.Minute):
			t.Fatal("timed out waiting for the deploy to fail")
		}
	}

	// wait for jobs to come back up
	hosts, err := s.clusterClient(t).Hosts()
	expected := map[string]map[ct.JobState]int{
		"web":       {ct.JobStateUp: formation.Processes["web"]},
		"scheduler": {ct.JobStateUp: len(hosts)},
	}
	t.Assert(watcher.WaitFor(expected, scaleTimeout, nil), c.IsNil)

	// check the correct controller jobs are running
	t.Assert(err, c.IsNil)
	t.Assert(hosts, c.Not(c.HasLen), 0)
	actual := make(map[string]map[string]int)
	for _, h := range hosts {
		jobs, err := h.ListJobs()
		t.Assert(err, c.IsNil)
		for _, job := range jobs {
			if job.Status != host.StatusRunning {
				continue
			}
			appID := job.Job.Metadata["flynn-controller.app"]
			if appID != app.ID {
				continue
			}
			releaseID := job.Job.Metadata["flynn-controller.release"]
			if releaseID != currentReleaseID {
				continue
			}
			if _, ok := actual[releaseID]; !ok {
				actual[releaseID] = make(map[string]int)
			}
			typ := job.Job.Metadata["flynn-controller.type"]
			actual[releaseID][typ]++
		}
	}
	t.Assert(actual, c.DeepEquals, map[string]map[string]int{
		currentReleaseID: {
			"web":       formation.Processes["web"],
			"scheduler": formation.Processes["scheduler"] * len(hosts),
			"worker":    formation.Processes["worker"],
		},
	})
}
Example #29
0
func (s *SchedulerSuite) TestControllerRestart(t *c.C) {
	// get the current controller details
	app, err := s.controllerClient(t).GetApp("controller")
	t.Assert(err, c.IsNil)
	release, err := s.controllerClient(t).GetAppRelease("controller")
	t.Assert(err, c.IsNil)
	formation, err := s.controllerClient(t).GetFormation(app.ID, release.ID)
	t.Assert(err, c.IsNil)
	list, err := s.controllerClient(t).JobList("controller")
	t.Assert(err, c.IsNil)
	var jobs []*ct.Job
	for _, job := range list {
		if job.Type == "web" && job.State == ct.JobStateUp {
			jobs = append(jobs, job)
		}
	}
	t.Assert(jobs, c.HasLen, formation.Processes["web"])
	jobID := jobs[0].ID
	hostID, _ := cluster.ExtractHostID(jobID)
	t.Assert(hostID, c.Not(c.Equals), "")
	debugf(t, "current controller app[%s] host[%s] job[%s]", app.ID, hostID, jobID)

	// subscribe to service events, wait for current event
	events := make(chan *discoverd.Event)
	stream, err := s.discoverdClient(t).Service("controller").Watch(events)
	t.Assert(err, c.IsNil)
	defer stream.Close()
	type serviceEvents map[discoverd.EventKind]int
	wait := func(expected serviceEvents) {
		actual := make(serviceEvents)
	outer:
		for {
			select {
			case event := <-events:
				actual[event.Kind]++
				for kind, count := range expected {
					if actual[kind] != count {
						continue outer
					}
				}
				return
			case <-time.After(scaleTimeout):
				t.Fatal("timed out waiting for controller service event")
			}
		}
	}
	wait(serviceEvents{discoverd.EventKindCurrent: 1})

	// start another controller and wait for it to come up
	debug(t, "scaling the controller up")
	formation.Processes["web"]++
	t.Assert(s.controllerClient(t).PutFormation(formation), c.IsNil)
	wait(serviceEvents{discoverd.EventKindUp: 1})

	// kill the first controller and check the scheduler brings it back online
	cc := cluster.NewClientWithServices(s.discoverdClient(t).Service)
	hc, err := cc.Host(hostID)
	t.Assert(err, c.IsNil)
	debug(t, "stopping job ", jobID)
	t.Assert(hc.StopJob(jobID), c.IsNil)
	wait(serviceEvents{discoverd.EventKindUp: 1, discoverd.EventKindDown: 1})

	// scale back down
	debug(t, "scaling the controller down")
	formation.Processes["web"]--
	t.Assert(s.controllerClient(t).PutFormation(formation), c.IsNil)
	wait(serviceEvents{discoverd.EventKindDown: 1})

	// unset the suite's client so other tests use a new client
	s.controller = nil
}
Example #30
0
// This test emulates deploys in the dashboard app
func (s *TaffyDeploySuite) TestDeploys(t *c.C) {
	assertMeta := func(m map[string]string, k string, checker c.Checker, args ...interface{}) {
		v, ok := m[k]
		t.Assert(ok, c.Equals, true)
		t.Assert(v, checker, args...)
	}

	client := s.controllerClient(t)

	github := map[string]string{
		"user":      "******",
		"repo":      "nodejs-flynn-example",
		"branch":    "master",
		"rev":       "5e177fec38fbde7d0a03e9e8dccf8757c68caa11",
		"clone_url": "https://github.com/flynn-examples/nodejs-flynn-example.git",
	}

	// initial deploy

	app := &ct.App{}
	t.Assert(client.CreateApp(app), c.IsNil)
	debugf(t, "created app %s (%s)", app.Name, app.ID)

	env := map[string]string{
		"SOMEVAR": "SOMEVAL",
	}
	meta := map[string]string{
		"github":      "true",
		"github_user": github["user"],
		"github_repo": github["repo"],
	}
	s.deployWithTaffy(t, app, env, meta, github)

	release, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)
	t.Assert(release, c.NotNil)
	t.Assert(release.Meta, c.NotNil)
	assertMeta(release.Meta, "git", c.Equals, "true")
	assertMeta(release.Meta, "clone_url", c.Equals, github["clone_url"])
	assertMeta(release.Meta, "branch", c.Equals, github["branch"])
	assertMeta(release.Meta, "rev", c.Equals, github["rev"])
	assertMeta(release.Meta, "taffy_job", c.Not(c.Equals), "")
	assertMeta(release.Meta, "github", c.Equals, "true")
	assertMeta(release.Meta, "github_user", c.Equals, github["user"])
	assertMeta(release.Meta, "github_repo", c.Equals, github["repo"])
	t.Assert(release.Env, c.NotNil)
	assertMeta(release.Env, "SOMEVAR", c.Equals, "SOMEVAL")

	// second deploy

	github["rev"] = "4231f8871da2b9fd73a5402753df3dfc5609d7b7"

	release, err = client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)

	s.deployWithTaffy(t, app, env, meta, github)

	newRelease, err := client.GetAppRelease(app.ID)
	t.Assert(err, c.IsNil)
	t.Assert(newRelease.ID, c.Not(c.Equals), release.ID)
	env["SLUG_URL"] = newRelease.Env["SLUG_URL"] // SLUG_URL will be different
	t.Assert(env, c.DeepEquals, newRelease.Env)
	t.Assert(release.Processes, c.DeepEquals, newRelease.Processes)
	t.Assert(newRelease, c.NotNil)
	t.Assert(newRelease.Meta, c.NotNil)
	assertMeta(newRelease.Meta, "git", c.Equals, "true")
	assertMeta(newRelease.Meta, "clone_url", c.Equals, github["clone_url"])
	assertMeta(newRelease.Meta, "branch", c.Equals, github["branch"])
	assertMeta(newRelease.Meta, "rev", c.Equals, github["rev"])
	assertMeta(newRelease.Meta, "taffy_job", c.Not(c.Equals), "")
	assertMeta(newRelease.Meta, "github", c.Equals, "true")
	assertMeta(newRelease.Meta, "github_user", c.Equals, github["user"])
	assertMeta(newRelease.Meta, "github_repo", c.Equals, github["repo"])
}