Beispiel #1
0
func (c *Cluster) bootstrapLayer1(instances []*Instance) error {
	inst := instances[0]
	c.ClusterDomain = fmt.Sprintf("flynn-%s.local", random.String(16))
	c.ControllerKey = random.String(16)
	c.BackoffPeriod = 5 * time.Second
	rd, wr := io.Pipe()

	ips := make([]string, len(instances))
	for i, inst := range instances {
		ips[i] = inst.IP
	}

	var cmdErr error
	go func() {
		command := fmt.Sprintf(
			"CLUSTER_DOMAIN=%s CONTROLLER_KEY=%s BACKOFF_PERIOD=%fs flynn-host bootstrap --json --min-hosts=%d --peer-ips=%s /etc/flynn-bootstrap.json",
			c.ClusterDomain, c.ControllerKey, c.BackoffPeriod.Seconds(), len(instances), strings.Join(ips, ","),
		)
		cmdErr = inst.Run(command, &Streams{Stdout: wr, Stderr: os.Stderr})
		wr.Close()
	}()

	// grab the controller tls pin from the bootstrap output
	var cert controllerCert
	dec := json.NewDecoder(rd)
	for {
		var msg bootstrapMsg
		if err := dec.Decode(&msg); err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("failed to parse bootstrap JSON output: %s", err)
		}
		c.log("bootstrap ===>", msg.Id, msg.State)
		if msg.State == "error" {
			c.log(msg.Error)
		}
		if msg.Id == "controller-cert" && msg.State == "done" {
			json.Unmarshal(msg.Data, &cert)
		}
	}
	if cmdErr != nil {
		return cmdErr
	}
	if cert.Pin == "" {
		return errors.New("could not determine controller cert from bootstrap output")
	}
	c.ControllerPin = cert.Pin

	// grab the router IP from discoverd
	disc := discoverd.NewClientWithURL(fmt.Sprintf("http://%s:1111", inst.IP))
	leader, err := disc.Service("router-api").Leader()
	if err != nil {
		return fmt.Errorf("could not detect router ip: %s", err)
	}
	if err = setLocalDNS([]string{c.ClusterDomain, c.ControllerDomain()}, leader.Host()); err != nil {
		return fmt.Errorf("could not set cluster DNS entries: %s", err)
	}
	c.RouterIP = leader.Host()
	return nil
}
Beispiel #2
0
func (s *CLISuite) TestExportImport(t *c.C) {
	srcApp := "app-export" + random.String(8)
	dstApp := "app-import" + random.String(8)

	// create and push app+db
	r := s.newGitRepo(t, "http")
	t.Assert(r.flynn("create", srcApp), Succeeds)
	t.Assert(r.flynn("key", "add", r.ssh.Pub), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)
	t.Assert(r.flynn("resource", "add", "postgres"), Succeeds)
	t.Assert(r.flynn("pg", "psql", "--", "-c",
		"CREATE table foos (data text); INSERT INTO foos (data) VALUES ('foobar')"), Succeeds)

	// export app
	file := filepath.Join(t.MkDir(), "export.tar")
	t.Assert(r.flynn("export", "-f", file), Succeeds)

	// remove db table from source app
	t.Assert(r.flynn("pg", "psql", "--", "-c", "DROP TABLE foos"), Succeeds)

	// import app
	t.Assert(r.flynn("import", "--name", dstApp, "--file", file), Succeeds)

	// test db was imported
	query := r.flynn("-a", dstApp, "pg", "psql", "--", "-c", "SELECT * FROM foos")
	t.Assert(query, SuccessfulOutputContains, "foobar")

	// wait for it to start
	_, err := s.discoverdClient(t).Instances(dstApp+"-web", 10*time.Second)
	t.Assert(err, c.IsNil)
}
Beispiel #3
0
func (s *CLISuite) TestExportImport(t *c.C) {
	srcApp := "app-export" + random.String(8)
	dstApp := "app-import" + random.String(8)

	// create app
	r := s.newGitRepo(t, "http")
	t.Assert(r.flynn("create", srcApp), Succeeds)

	// exporting the app without a release should work
	file := filepath.Join(t.MkDir(), "export.tar")
	t.Assert(r.flynn("export", "-f", file), Succeeds)
	assertExportContains := func(paths ...string) {
		cmd := r.sh(fmt.Sprintf("tar --list --file=%s --strip=1 --show-transformed", file))
		t.Assert(cmd, Outputs, strings.Join(paths, "\n")+"\n")
	}
	assertExportContains("app.json", "routes.json")

	// exporting the app with an artifact-less release should work
	t.Assert(r.flynn("env", "set", "FOO=BAR"), Succeeds)
	t.Assert(r.flynn("export", "-f", file), Succeeds)
	assertExportContains("app.json", "routes.json", "release.json")

	// release the app and provision some dbs
	t.Assert(r.git("push", "flynn", "master"), Succeeds)
	t.Assert(r.flynn("resource", "add", "postgres"), Succeeds)
	t.Assert(r.flynn("pg", "psql", "--", "-c",
		"CREATE table foos (data text); INSERT INTO foos (data) VALUES ('foobar')"), Succeeds)
	t.Assert(r.flynn("resource", "add", "mysql"), Succeeds)
	t.Assert(r.flynn("mysql", "console", "--", "-e",
		"CREATE TABLE foos (data TEXT); INSERT INTO foos (data) VALUES ('foobar')"), Succeeds)

	// export app
	t.Assert(r.flynn("export", "-f", file), Succeeds)
	assertExportContains(
		"app.json", "routes.json", "release.json", "artifact.json",
		"formation.json", "slug.tar.gz", "postgres.dump", "mysql.dump",
	)

	// remove db tables from source app
	t.Assert(r.flynn("pg", "psql", "--", "-c", "DROP TABLE foos"), Succeeds)
	t.Assert(r.flynn("mysql", "console", "--", "-e", "DROP TABLE foos"), Succeeds)

	// remove the git remote
	t.Assert(r.git("remote", "remove", "flynn"), Succeeds)

	// import app
	t.Assert(r.flynn("import", "--name", dstApp, "--file", file), Succeeds)

	// test dbs were imported
	query := r.flynn("-a", dstApp, "pg", "psql", "--", "-c", "SELECT * FROM foos")
	t.Assert(query, SuccessfulOutputContains, "foobar")
	query = r.flynn("-a", dstApp, "mysql", "console", "--", "-e", "SELECT * FROM foos")
	t.Assert(query, SuccessfulOutputContains, "foobar")

	// wait for it to start
	_, err := s.discoverdClient(t).Instances(dstApp+"-web", 10*time.Second)
	t.Assert(err, c.IsNil)
}
Beispiel #4
0
func (c *Cluster) bootstrapFlynn() error {
	inst := c.instances[0]
	c.ControllerDomain = fmt.Sprintf("flynn-%s.local", random.String(16))
	c.ControllerKey = random.String(16)
	rd, wr := io.Pipe()
	var cmdErr error
	go func() {
		command := fmt.Sprintf(
			"docker run -e=DISCOVERD=%s:1111 -e CONTROLLER_DOMAIN=%s -e CONTROLLER_KEY=%s flynn/bootstrap -json -min-hosts=%d /etc/manifest.json",
			inst.IP(), c.ControllerDomain, c.ControllerKey, len(c.instances),
		)
		cmdErr = inst.Run(command, attempts, wr, os.Stderr)
		wr.Close()
	}()

	// grab the controller tls pin from the bootstrap output
	var cert controllerCert
	dec := json.NewDecoder(rd)
	for {
		var msg bootstrapMsg
		if err := dec.Decode(&msg); err == io.EOF {
			break
		} else if err != nil {
			return fmt.Errorf("failed to parse bootstrap JSON output: %s", err)
		}
		c.log("bootstrap ===>", msg.Id, msg.State)
		if msg.State == "error" {
			c.log(msg)
		}
		if msg.Id == "controller-cert" && msg.State == "done" {
			json.Unmarshal(msg.Data, &cert)
		}
	}
	if cmdErr != nil {
		return cmdErr
	}
	if cert.Pin == "" {
		return errors.New("could not determine controller cert from bootstrap output")
	}
	c.ControllerPin = cert.Pin

	// grab the router IP from discoverd
	discoverd.Connect(inst.IP() + ":1111")
	set, err := discoverd.NewServiceSet("router-api")
	if err != nil {
		return fmt.Errorf("could not detect router ip: %s", err)
	}
	defer set.Close()
	leader := set.Leader()
	if leader == nil {
		return errors.New("could not detect router ip: no router-api leader")
	}
	if err = setLocalDNS(c.ControllerDomain, leader.Host); err != nil {
		return fmt.Errorf("could not set router DNS entry: %s", err)
	}
	return nil
}
Beispiel #5
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].ArtifactIDs[1])
	t.Assert(err, c.IsNil)
	s.assertURI(t, slugArtifact.URI, http.StatusOK)
	slugLayerURL := slugArtifact.LayerURL(slugArtifact.Manifest().Rootfs[0].Layers[0])
	s.assertURI(t, slugLayerURL, 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 2 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)
	s.assertURI(t, slugLayerURL, http.StatusNotFound)

	// check the image artifact was not deleted (since it is shared between both releases)
	_, err = client.GetArtifact(releases[1].ArtifactIDs[0])
	t.Assert(err, c.IsNil)
}
Beispiel #6
0
func (s *BasicSuite) TestBasic(t *c.C) {
	name := random.String(30)
	t.Assert(s.Flynn("create", name), Outputs, fmt.Sprintf("Created %s\n", name))

	push := s.Git("push", "flynn", "master")
	t.Assert(push, OutputContains, "Node.js app detected")
	t.Assert(push, OutputContains, "Downloading and installing node")
	t.Assert(push, OutputContains, "Installing dependencies")
	t.Assert(push, OutputContains, "Procfile declares types -> web")
	t.Assert(push, OutputContains, "Creating release")
	t.Assert(push, OutputContains, "Application deployed")
	t.Assert(push, OutputContains, "* [new branch]      master -> master")

	t.Assert(s.Flynn("scale", "web=3"), Succeeds)

	newRoute := s.Flynn("route-add-http", random.String(32)+".dev")
	t.Assert(newRoute, Succeeds)

	t.Assert(s.Flynn("routes"), OutputContains, strings.TrimSpace(newRoute.Output))

	// use Attempts to give the processes time to start
	if err := Attempts.Run(func() error {
		ps := s.Flynn("ps")
		if ps.Err != nil {
			return ps.Err
		}
		psLines := strings.Split(strings.TrimSpace(ps.Output), "\n")
		if len(psLines) != 4 {
			return fmt.Errorf("Expected 4 ps lines, got %d", len(psLines))
		}

		for _, l := range psLines[1:] {
			idType := regexp.MustCompile(`\s+`).Split(l, 2)
			if idType[1] != "web" {
				return fmt.Errorf("Expected web type, got %s", idType[1])
			}
			log := s.Flynn("log", idType[0])
			if !strings.Contains(log.Output, "Listening on ") {
				return fmt.Errorf("Expected \"%s\" to contain \"Listening on \"", log.Output)
			}
		}
		return nil
	}); err != nil {
		t.Error(err)
	}

	// Make HTTP requests
}
Beispiel #7
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)
}
Beispiel #8
0
func (ZpoolTests) TestProviderAutomaticFileVdevZpoolCreation(c *C) {
	dataset := "testpool-dinosaur"

	// don't actually use ioutil.Tempfile;
	// we want to exerise the path where the file doesn't exist.
	backingFilePath := fmt.Sprintf("/tmp/zfs-%s", random.String(12))
	defer os.Remove(backingFilePath)

	provider, err := NewProvider(&ProviderConfig{
		DatasetName: dataset,
		Make: &MakeDev{
			BackingFilename: backingFilePath,
			Size:            one_gig,
		},
	})
	defer func() {
		pool, _ := gzfs.GetZpool(dataset)
		if pool != nil {
			pool.Destroy()
		}
	}()
	c.Assert(err, IsNil)
	c.Assert(provider, NotNil)

	// also, we shouldn't get any '/testpool' dir at root
	_, err = os.Stat(dataset)
	c.Assert(err, NotNil)
	c.Assert(os.IsNotExist(err), Equals, true)
}
Beispiel #9
0
func (s *S) TestFormationStreamingInterrupted(c *C) {
	before := time.Now()
	appRepo := NewAppRepo(s.hc.db, os.Getenv("DEFAULT_ROUTE_DOMAIN"), s.hc.rc)
	releaseRepo := NewReleaseRepo(s.hc.db)
	artifactRepo := NewArtifactRepo(s.hc.db)
	formationRepo := NewFormationRepo(s.hc.db, appRepo, releaseRepo, artifactRepo)

	artifact := &ct.Artifact{Type: "docker", URI: fmt.Sprintf("https://example.com/%s", random.String(8))}
	c.Assert(artifactRepo.Add(artifact), IsNil)

	release := &ct.Release{ArtifactID: artifact.ID}
	c.Assert(releaseRepo.Add(release), IsNil)

	app := &ct.App{Name: "streamtest-interrupted"}
	c.Assert(appRepo.Add(app), IsNil)

	formation := &ct.Formation{ReleaseID: release.ID, AppID: app.ID}
	c.Assert(formationRepo.Add(formation), IsNil)

	ch := make(chan *ct.ExpandedFormation)
	updated := make(chan struct{})

	_, err := formationRepo.Subscribe(ch, before, updated)
	c.Assert(err, IsNil)

	// simulate scenario where we have not completed `sendUpdatedSince` but the channel for a subscription
	// is closed, by example due to an error listening on table `formations` that triggered `unsubscribeAll`.
	formationRepo.unsubscribeAll()

	// wait until `sendUpdateSince` finishes at which point it will not longer send to the (now closed) channel.
	<-updated
}
Beispiel #10
0
func New(bc BootConfig, out io.Writer) *Cluster {
	return &Cluster{
		ID:  random.String(8),
		bc:  bc,
		out: out,
	}
}
Beispiel #11
0
func (t *TapManager) NewTap(uid, gid int) (*Tap, error) {
	tap := &Tap{Name: "flynntap." + random.String(5), bridge: t.bridge}

	if err := createTap(tap.Name, uid, gid); err != nil {
		return nil, err
	}

	var err error
	tap.IP, err = t.bridge.alloc.RequestIP(t.bridge.ipNet, nil)
	if err != nil {
		tap.Close()
		return nil, err
	}

	iface, err := net.InterfaceByName(tap.Name)
	if err != nil {
		tap.Close()
		return nil, err
	}
	if err := netlink.NetworkLinkUp(iface); err != nil {
		tap.Close()
		return nil, err
	}
	if err := netlink.AddToBridge(iface, t.bridge.iface); err != nil {
		tap.Close()
		return nil, err
	}

	return tap, nil
}
Beispiel #12
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)
}
Beispiel #13
0
/*
	Make an 'ish' application on the given host, returning it when
	it has registered readiness with discoverd.

	User will want to defer cmd.Kill() to clean up.
*/
func makeIshApp(cluster *cluster.Client, h *cluster.Host, dc *discoverd.Client, extraConfig host.ContainerConfig) (*exec.Cmd, *discoverd.Instance, error) {
	// pick a unique string to use as service name so this works with concurrent tests.
	serviceName := "ish-service-" + random.String(6)

	// run a job that accepts tcp connections and performs tasks we ask of it in its container
	cmd := exec.JobUsingCluster(cluster, exec.DockerImage(imageURIs["test-apps"]), &host.Job{
		Config: host.ContainerConfig{
			Args:  []string{"/bin/ish"},
			Ports: []host.Port{{Proto: "tcp"}},
			Env: map[string]string{
				"NAME": serviceName,
			},
		}.Merge(extraConfig),
	})
	cmd.HostID = h.ID()
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}

	// wait for the job to heartbeat and return its address
	services, err := dc.Instances(serviceName, time.Second*100)
	if err != nil {
		cmd.Kill()
		return nil, nil, err
	}
	if len(services) != 1 {
		cmd.Kill()
		return nil, nil, fmt.Errorf("test setup: expected exactly one service instance, got %d", len(services))
	}

	return cmd, services[0], nil
}
Beispiel #14
0
func (s *CLISuite) TestReleaseRollback(t *c.C) {
	// create an app and release it
	r := s.newGitRepo(t, "http")
	app := "release-rollback-" + random.String(8)
	t.Assert(r.flynn("create", app), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)

	// check that rollback fails when there's only a single release
	res := r.flynn("release", "rollback", "--yes")
	t.Assert(res, c.Not(Succeeds))

	// create a second release
	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)

	// rollback to the second release
	res = r.flynn("release", "rollback", "--yes")
	t.Assert(res, Succeeds)

	// revert rollback
	res = r.flynn("release", "rollback", "--yes", releases[0].ID)
	t.Assert(res, Succeeds)

	// check that attempting to rollback to the current release fails
	res = r.flynn("release", "rollback", "--yes", releases[0].ID)
	t.Assert(res, c.Not(Succeeds))
}
Beispiel #15
0
func (s *ControllerSuite) TestRouteEvents(t *c.C) {
	app := "app-route-events-" + random.String(8)
	client := s.controllerClient(t)

	// create and push app
	r := s.newGitRepo(t, "http")
	t.Assert(r.flynn("create", app), Succeeds)
	t.Assert(r.flynn("key", "add", r.ssh.Pub), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)

	// wait for it to start
	service := app + "-web"
	_, err := s.discoverdClient(t).Instances(service, 10*time.Second)
	t.Assert(err, c.IsNil)

	// stream events
	events := make(chan *ct.Event)
	stream, err := client.StreamEvents(controller.StreamEventsOptions{
		AppID:       app,
		ObjectTypes: []ct.EventType{ct.EventTypeRoute, ct.EventTypeRouteDeletion},
		Past:        true,
	}, events)
	t.Assert(err, c.IsNil)
	defer stream.Close()

	assertEventType := func(typ ct.EventType) {
		select {
		case event, ok := <-events:
			t.Assert(ok, c.Equals, true)
			t.Assert(event.ObjectType, c.Equals, typ, c.Commentf("event: %#v", event))
		case <-time.After(30 * time.Second):
			t.Assert(true, c.Equals, false, c.Commentf("timed out waiting for %s event", string(typ)))
		}
	}

	// default app route
	assertEventType(ct.EventTypeRoute)

	// create some routes
	routes := []string{"baz.example.com"}
	for _, route := range routes {
		t.Assert(r.flynn("route", "add", "http", route), Succeeds)
		assertEventType(ct.EventTypeRoute)
	}
	routeList, err := client.RouteList(app)
	t.Assert(err, c.IsNil)
	numRoutes := len(routes) + 1 // includes default app route
	t.Assert(routeList, c.HasLen, numRoutes)

	// delete app
	cmd := r.flynn("delete", "--yes")
	t.Assert(cmd, Succeeds)

	// check route deletion event
	assertEventType(ct.EventTypeRouteDeletion)
}
Beispiel #16
0
func newBuild(commit, description string, merge bool) *Build {
	now := time.Now()
	return &Build{
		Id:          now.Format("20060102150405") + "-" + random.String(8),
		CreatedAt:   &now,
		Commit:      commit,
		Description: description,
		Merge:       merge,
	}
}
Beispiel #17
0
func (s *S) createTestArtifact(c *C, in *ct.Artifact) *ct.Artifact {
	if in.Type == "" {
		in.Type = "docker"
	}
	if in.URI == "" {
		in.URI = fmt.Sprintf("https://example.com/%s", random.String(8))
	}
	c.Assert(s.c.CreateArtifact(in), IsNil)
	return in
}
Beispiel #18
0
func (s *CLISuite) TestProvider(t *c.C) {
	t.Assert(s.flynn(t, "provider"), SuccessfulOutputContains, "postgres")

	// flynn provider add
	testProvider := "test-provider" + random.String(8)
	testProviderUrl := "http://testprovider.discoverd"
	cmd := s.flynn(t, "provider", "add", testProvider, testProviderUrl)
	t.Assert(cmd, Outputs, fmt.Sprintf("Created provider %s.\n", testProvider))
	t.Assert(s.flynn(t, "provider"), SuccessfulOutputContains, testProvider)
}
Beispiel #19
0
func (s *CLISuite) TestDeploy(t *c.C) {
	// create and push app
	r := s.newGitRepo(t, "http")
	t.Assert(r.flynn("create", "deploy-"+random.String(8)), Succeeds)
	t.Assert(r.git("push", "flynn", "master"), Succeeds)

	deploy := r.flynn("deployment")
	t.Assert(deploy, Succeeds)
	t.Assert(deploy.Output, Matches, "complete")
}
Beispiel #20
0
func (r *Runner) save(b *Build) error {
	if b.Id == "" {
		b.Id = b.CreatedAt.Format("20060102150405") + "-" + random.String(8)
	}
	return r.db.Update(func(tx *bolt.Tx) error {
		val, err := json.Marshal(b)
		if err != nil {
			return err
		}
		return tx.Bucket(dbBucket).Put([]byte(b.Id), val)
	})
}
Beispiel #21
0
func (ZpoolTests) TestOrphanedZpoolFileAdoption(c *C) {
	dataset := "testpool-bananagram"

	backingFilePath := fmt.Sprintf("/tmp/zfs-%s", random.String(12))
	defer os.Remove(backingFilePath)

	provider, err := NewProvider(&ProviderConfig{
		DatasetName: dataset,
		Make: &MakeDev{
			BackingFilename: backingFilePath,
			Size:            one_gig,
		},
	})
	defer func() {
		pool, _ := gzfs.GetZpool(dataset)
		if pool != nil {
			pool.Destroy()
		}
	}()
	c.Assert(err, IsNil)
	c.Assert(provider, NotNil)

	// add a dataset to this zpool, so we can check for it on the flip side
	markerDatasetName := path.Join(dataset, "testfs")
	_, err = gzfs.CreateFilesystem(markerDatasetName, nil)
	c.Assert(err, IsNil)

	// do a 'zpool export'
	// this roughly approximates what we see after a host reboot
	// (though host reboots may leave it in an even more unclean state than this)
	err = exec.Command("zpool", "export", "-f", dataset).Run()
	c.Assert(err, IsNil)

	// sanity check that our test is doing the right thing: zpool forgot about these
	_, err = gzfs.GetDataset(dataset)
	c.Assert(err, NotNil)
	_, err = gzfs.GetDataset(markerDatasetName)
	c.Assert(err, NotNil)

	// if we create another provider with the same file vdev path, it should
	// pick up that file again without wrecking the dataset
	provider, err = NewProvider(&ProviderConfig{
		DatasetName: dataset,
		Make: &MakeDev{
			BackingFilename: backingFilePath,
			Size:            one_gig,
		},
	})
	c.Assert(err, IsNil)
	c.Assert(provider, NotNil)
	_, err = gzfs.GetDataset(markerDatasetName)
	c.Assert(err, IsNil)
}
Beispiel #22
0
func (s *S) createTestArtifact(c *C, in *ct.Artifact) *ct.Artifact {
	if in.Type == "" {
		in.Type = ct.ArtifactTypeFlynn
		in.RawManifest = ct.ImageManifest{
			Type: ct.ImageManifestTypeV1,
		}.RawManifest()
	}
	if in.URI == "" {
		in.URI = fmt.Sprintf("https://example.com/%s", random.String(8))
	}
	c.Assert(s.c.CreateArtifact(in), IsNil)
	return in
}
Beispiel #23
0
func parseEnviron() map[string]string {
	env := os.Environ()
	res := make(map[string]string, len(env))
	for _, v := range env {
		kv := strings.SplitN(v, "=", 2)
		res[kv[0]] = kv[1]
	}

	if _, ok := res["ETCD_NAME"]; !ok {
		res["ETCD_NAME"] = random.String(8)
	}

	return res
}
Beispiel #24
0
func (s *CLISuite) TestRemote(t *c.C) {
	remoteApp := "remote-" + random.String(8)
	customRemote := random.String(8)

	r := s.newGitRepoWithoutTrace(t, "http")
	// create app without remote
	t.Assert(r.flynn("create", remoteApp, "--remote", `""`), Succeeds)

	// ensure no remotes exist
	t.Assert(r.git("remote").Output, c.Equals, "\"\"\n")
	// create the default remote
	t.Assert(r.flynn("-a", remoteApp, "remote", "add"), Succeeds)
	// ensure the default remote exists
	t.Assert(r.git("remote", "show", "flynn"), Succeeds)
	// now delete it
	t.Assert(r.git("remote", "rm", "flynn"), Succeeds)

	// ensure no remotes exist
	t.Assert(r.git("remote").Output, c.Equals, "\"\"\n")
	// create a custom remote
	t.Assert(r.flynn("-a", remoteApp, "remote", "add", customRemote), Succeeds)
	// ensure the custom remote exists
	t.Assert(r.git("remote", "show", customRemote), Succeeds)
}
Beispiel #25
0
func (c *Cluster) setup() error {
	if _, err := os.Stat(c.bc.Kernel); os.IsNotExist(err) {
		return fmt.Errorf("cluster: not a kernel file: %s", c.bc.Kernel)
	}
	if c.bridge == nil {
		var err error
		name := "flynnbr." + random.String(5)
		c.logf("creating network bridge %s\n", name)
		c.bridge, err = createBridge(name, c.bc.Network, c.bc.NatIface)
		if err != nil {
			return fmt.Errorf("could not create network bridge: %s", err)
		}
	}
	c.vm = NewVMManager(c.bridge)
	return nil
}
Beispiel #26
0
// https://github.com/flynn/flynn/issues/2257
func (s *ControllerSuite) TestResourceProvisionRecreatedApp(t *c.C) {
	app := "app-recreate-" + random.String(8)
	client := s.controllerClient(t)

	// create, delete, and recreate app
	r := s.newGitRepo(t, "http")
	t.Assert(r.flynn("create", app), Succeeds)
	t.Assert(r.flynn("delete", "--yes"), Succeeds)
	t.Assert(r.flynn("create", app), Succeeds)

	// provision resource
	t.Assert(r.flynn("resource", "add", "postgres"), Succeeds)
	resources, err := client.AppResourceList(app)
	t.Assert(err, c.IsNil)
	t.Assert(resources, c.HasLen, 1)
}
Beispiel #27
0
func NewLog(l *lumberjack.Logger) *Log {
	if l == nil {
		l = &lumberjack.Logger{}
	}
	if l.MaxSize == 0 {
		l.MaxSize = 100 // megabytes
	}
	if l.Filename == "" {
		l.Filename = path.Join(os.TempDir(), random.String(16)+".log")
	}
	l.Rotate() // force creating a log file straight away
	log := &Log{
		l:      l,
		closed: make(chan struct{}),
	}
	return log
}
Beispiel #28
0
func (r *Runner) save(b *Build) error {
	if b.Id == "" {
		b.Id = random.String(8)
	}
	return r.db.Update(func(tx *bolt.Tx) error {
		bkt := tx.Bucket([]byte("pending-builds"))
		if b.State == "pending" {
			val, err := json.Marshal(b)
			if err != nil {
				return err
			}
			return bkt.Put([]byte(b.Id), val)
		} else {
			return bkt.Delete([]byte(b.Id))
		}
	})
}
Beispiel #29
0
func (s *TempZpool) SetUpTest(c *C) {
	if s.IDstring == "" {
		s.IDstring = random.String(12)
	}

	// Set up a new provider with a zpool that will be destroyed on teardown
	s.ZpoolVdevFilePath = fmt.Sprintf("/tmp/flynn-test-zpool-%s.vdev", s.IDstring)
	s.ZpoolName = fmt.Sprintf("flynn-test-zpool-%s", s.IDstring)
	var err error
	s.VolProv, err = NewProvider(&ProviderConfig{
		DatasetName: s.ZpoolName,
		Make: &MakeDev{
			BackingFilename: s.ZpoolVdevFilePath,
			Size:            int64(math.Pow(2, float64(30))),
		},
	})
	c.Assert(err, IsNil)
}
Beispiel #30
0
func (v *VMManager) NewInstance(c *VMConfig) (*Instance, error) {
	var err error
	inst := &Instance{ID: random.String(8), VMConfig: c}
	if c.Kernel == "" {
		c.Kernel = "vmlinuz"
	}
	if c.Out == nil {
		c.Out, err = os.Create("flynn-" + inst.ID + ".log")
		if err != nil {
			return nil, err
		}
	}
	inst.tap, err = v.taps.NewTap(c.User, c.Group)
	if err != nil {
		return nil, err
	}
	inst.IP = inst.tap.RemoteIP.String()
	return inst, nil
}