Beispiel #1
0
func (s *S) createLogTestApp(c *C, name string, stream io.Reader) (*ct.App, string, string) {
	app := s.createTestApp(c, &ct.App{Name: name})
	hostID, jobID := utils.UUID(), utils.UUID()
	hc := tu.NewFakeHostClient(hostID)
	hc.SetAttach(jobID, newFakeLog(stream))
	s.cc.SetHostClient(hostID, hc)
	return app, hostID, jobID
}
Beispiel #2
0
func (s *S) TestKillJob(c *C) {
	app := s.createTestApp(c, &ct.App{Name: "killjob"})
	hostID, jobID := utils.UUID(), utils.UUID()
	hc := tu.NewFakeHostClient(hostID)
	s.cc.SetHostClient(hostID, hc)

	res, err := s.Delete("/apps/" + app.ID + "/jobs/" + hostID + "-" + jobID)
	c.Assert(err, IsNil)
	c.Assert(res.StatusCode, Equals, 200)
	c.Assert(hc.IsStopped(jobID), Equals, true)
}
func (s *S) TestPutResource(c *C) {
	app := s.createTestApp(c, &ct.App{Name: "put-resource"})
	provider := s.createTestProvider(c, &ct.Provider{URL: "https://example.ca", Name: "put-resource"})

	resource := &ct.Resource{
		ExternalID: "/foo/bar",
		Env:        map[string]string{"FOO": "BAR"},
		Apps:       []string{app.ID},
	}
	id := utils.UUID()
	path := fmt.Sprintf("/providers/%s/resources/%s", provider.ID, id)
	created := &ct.Resource{}
	_, err := s.Put(path, resource, created)
	c.Assert(err, IsNil)

	c.Assert(created.ID, Equals, id)
	c.Assert(created.ProviderID, Equals, provider.ID)
	c.Assert(created.Env, DeepEquals, resource.Env)
	c.Assert(created.Apps, DeepEquals, resource.Apps)
	c.Assert(created.CreatedAt, Not(IsNil))

	gotResource := &ct.Resource{}
	_, err = s.Get(path, gotResource)
	c.Assert(err, IsNil)
	c.Assert(gotResource, DeepEquals, created)
}
Beispiel #4
0
func (s *S) TestCreateArtifact(c *C) {
	for i, id := range []string{"", utils.UUID()} {
		in := &ct.Artifact{
			ID:   id,
			Type: "docker-image",
			URI:  fmt.Sprintf("docker://flynn/host?id=adsf%d", i),
		}
		out := s.createTestArtifact(c, in)

		c.Assert(out.Type, Equals, in.Type)
		c.Assert(out.URI, Equals, in.URI)
		c.Assert(out.ID, Not(Equals), "")
		if id != "" {
			c.Assert(out.ID, Equals, id)
		}

		gotArtifact := &ct.Artifact{}
		res, err := s.Get("/artifacts/"+out.ID, gotArtifact)
		c.Assert(err, IsNil)
		c.Assert(gotArtifact, DeepEquals, out)

		res, err = s.Get("/artifacts/fail"+out.ID, gotArtifact)
		c.Assert(res.StatusCode, Equals, 404)
	}
}
Beispiel #5
0
func (s *S) TestCreateApp(c *C) {
	// app with no name returns 400
	res, err := s.Post("/apps", &ct.App{}, &ct.App{})
	c.Assert(err, IsNil)
	c.Assert(res.StatusCode, Equals, 400)
	body, err := s.body(res)
	c.Assert(err, IsNil)
	c.Assert(body, Equals, `{"field":"name","message":"must not be blank"}`)

	for i, id := range []string{"", utils.UUID()} {
		name := fmt.Sprintf("create-app-%d", i)
		app := s.createTestApp(c, &ct.App{ID: id, Name: name, Protected: true, Meta: map[string]string{"foo": "bar"}})
		c.Assert(app.Name, Equals, name)
		c.Assert(app.ID, Not(Equals), "")
		if id != "" {
			c.Assert(app.ID, Equals, id)
		}
		c.Assert(app.Protected, Equals, true)
		c.Assert(app.Meta["foo"], Equals, "bar")

		gotApp := &ct.App{}
		res, err := s.Get("/apps/"+app.ID, gotApp)
		c.Assert(err, IsNil)
		c.Assert(gotApp, DeepEquals, app)

		res, err = s.Get("/apps/"+app.Name, gotApp)
		c.Assert(err, IsNil)
		c.Assert(gotApp, DeepEquals, app)

		res, err = s.Get("/apps/fail"+app.ID, gotApp)
		c.Assert(res.StatusCode, Equals, 404)
	}
}
Beispiel #6
0
func (r *AppRepo) Add(data interface{}) error {
	app := data.(*ct.App)
	if app.Name == "" {
		return ct.ValidationError{"name", "must not be blank"}
	}
	if len(app.Name) > 30 || !appNamePattern.MatchString(app.Name) {
		return ct.ValidationError{"name", "is invalid"}
	}
	if app.ID == "" {
		app.ID = utils.UUID()
	}
	var meta hstore.Hstore
	if len(app.Meta) > 0 {
		meta.Map = make(map[string]sql.NullString, len(app.Meta))
		for k, v := range app.Meta {
			meta.Map[k] = sql.NullString{String: v, Valid: true}
		}
	}
	err := r.db.QueryRow("INSERT INTO apps (app_id, name, protected, meta) VALUES ($1, $2, $3, $4) RETURNING created_at, updated_at", app.ID, app.Name, app.Protected, meta).Scan(&app.CreatedAt, &app.UpdatedAt)
	app.ID = cleanUUID(app.ID)
	if !app.Protected && r.defaultDomain != "" {
		route := (&strowger.HTTPRoute{
			Domain:  fmt.Sprintf("%s.%s", app.Name, r.defaultDomain),
			Service: app.Name + "-web",
		}).ToRoute()
		route.ParentRef = routeParentRef(app)
		if err := r.router.CreateRoute(route); err != nil {
			log.Printf("Error creating default route for %s: %s", app.Name, err)
		}
	}
	return err
}
Beispiel #7
0
func (r *fakeRouter) CreateRoute(route *strowger.Route) error {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	route.ID = route.Type + "/" + utils.UUID()
	now := time.Now()
	route.CreatedAt = &now
	route.UpdatedAt = &now
	r.routes[route.ID] = route
	return nil
}
Beispiel #8
0
func (s *S) TestCreateRelease(c *C) {
	for _, id := range []string{"", utils.UUID()} {
		in := &ct.Release{ID: id}
		out := s.createTestRelease(c, in)
		c.Assert(out.ArtifactID, Equals, in.ArtifactID)
		if id != "" {
			c.Assert(out.ID, Equals, id)
		}

		gotRelease := &ct.Release{}
		res, err := s.Get("/releases/"+out.ID, gotRelease)
		c.Assert(err, IsNil)
		c.Assert(gotRelease, DeepEquals, out)

		res, err = s.Get("/releases/fail"+out.ID, gotRelease)
		c.Assert(res.StatusCode, Equals, 404)
	}
}
Beispiel #9
0
func (r *ArtifactRepo) Add(data interface{}) error {
	a := data.(*ct.Artifact)
	// TODO: actually validate
	if a.ID == "" {
		a.ID = utils.UUID()
	}
	err := r.db.QueryRow("INSERT INTO artifacts (artifact_id, type, uri) VALUES ($1, $2, $3) RETURNING created_at",
		a.ID, a.Type, a.URI).Scan(&a.CreatedAt)
	if e, ok := err.(*pq.Error); ok && e.Code.Name() == "unique_violation" {
		err = r.db.QueryRow("SELECT artifact_id, created_at FROM artifacts WHERE type = $1 AND uri = $2",
			a.Type, a.URI).Scan(&a.ID, &a.CreatedAt)
		if err != nil {
			return err
		}
	}
	a.ID = cleanUUID(a.ID)
	return err
}
Beispiel #10
0
func (s *S) TestRunJobDetached(c *C) {
	app := s.createTestApp(c, &ct.App{Name: "run-detached"})

	hostID := utils.UUID()
	s.cc.SetHosts(map[string]host.Host{hostID: host.Host{}})

	artifact := s.createTestArtifact(c, &ct.Artifact{Type: "docker", URI: "docker://foo/bar"})
	release := s.createTestRelease(c, &ct.Release{
		ArtifactID: artifact.ID,
		Env:        map[string]string{"RELEASE": "true", "FOO": "bar"},
	})

	cmd := []string{"foo", "bar"}
	req := &ct.NewJob{
		ReleaseID: release.ID,
		Cmd:       cmd,
		Env:       map[string]string{"JOB": "true", "FOO": "baz"},
	}
	res := &ct.Job{}
	_, err := s.Post(fmt.Sprintf("/apps/%s/jobs", app.ID), req, res)
	c.Assert(err, IsNil)
	c.Assert(res.ID, Not(Equals), "")
	c.Assert(res.ReleaseID, Equals, release.ID)
	c.Assert(res.Type, Equals, "")
	c.Assert(res.Cmd, DeepEquals, cmd)

	job := s.cc.GetHost(hostID).Jobs[0]
	c.Assert(res.ID, Equals, hostID+"-"+job.ID)
	c.Assert(job.Attributes, DeepEquals, map[string]string{
		"flynn-controller.app":     app.ID,
		"flynn-controller.release": release.ID,
	})
	c.Assert(job.Config.Cmd, DeepEquals, []string{"foo", "bar"})
	sort.Strings(job.Config.Env)
	c.Assert(job.Config.Env, DeepEquals, []string{"FOO=baz", "JOB=true", "RELEASE=true"})
	c.Assert(job.Config.AttachStdout, Equals, true)
	c.Assert(job.Config.AttachStderr, Equals, true)
	c.Assert(job.Config.AttachStdin, Equals, false)
	c.Assert(job.Config.StdinOnce, Equals, false)
	c.Assert(job.Config.OpenStdin, Equals, false)
}
Beispiel #11
0
func (r *ReleaseRepo) Add(data interface{}) error {
	release := data.(*ct.Release)
	releaseCopy := *release

	releaseCopy.ID = ""
	releaseCopy.ArtifactID = ""
	releaseCopy.CreatedAt = nil
	data, err := json.Marshal(&releaseCopy)
	if err != nil {
		return err
	}
	if release.ID == "" {
		release.ID = utils.UUID()
	}

	err = r.db.QueryRow("INSERT INTO releases (release_id, artifact_id, data) VALUES ($1, $2, $3) RETURNING created_at",
		release.ID, release.ArtifactID, data).Scan(&release.CreatedAt)
	release.ID = cleanUUID(release.ID)
	release.ArtifactID = cleanUUID(release.ArtifactID)
	return err
}
Beispiel #12
0
func (rr *ResourceRepo) Add(r *ct.Resource) error {
	if r.ID == "" {
		r.ID = utils.UUID()
	}
	tx, err := rr.db.Begin()
	if err != nil {
		return err
	}
	err = tx.QueryRow(`INSERT INTO resources (resource_id, provider_id, external_id, env)
					   VALUES ($1, $2, $3, $4)
					   RETURNING created_at`,
		r.ID, r.ProviderID, r.ExternalID, envHstore(r.Env)).Scan(&r.CreatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}
	for i, appID := range r.Apps {
		var filterSQL string
		var args []interface{}
		if idPattern.MatchString(appID) {
			filterSQL = "app_id = $1 OR name = $2), $3)"
			args = []interface{}{appID, appID, r.ID}
		} else {
			filterSQL = "name = $1), $2)"
			args = []interface{}{appID, r.ID}
		}
		err = tx.QueryRow("INSERT INTO app_resources (app_id, resource_id) VALUES ((SELECT app_id FROM apps WHERE "+
			filterSQL+" RETURNING app_id", args...).Scan(&r.Apps[i])
		if err != nil {
			tx.Rollback()
			return err
		}
		r.Apps[i] = cleanUUID(r.Apps[i])
	}
	r.ID = cleanUUID(r.ID)
	return tx.Commit()
}
Beispiel #13
0
func (s *S) TestRunJobAttached(c *C) {
	app := s.createTestApp(c, &ct.App{Name: "run-attached"})
	hostID := utils.UUID()
	hc := tu.NewFakeHostClient(hostID)

	done := make(chan struct{})
	var jobID string
	hc.SetAttachFunc("*", func(req *host.AttachReq, wait bool) (cluster.ReadWriteCloser, func() error, error) {
		c.Assert(wait, Equals, true)
		c.Assert(req.JobID, Not(Equals), "")
		c.Assert(req, DeepEquals, &host.AttachReq{
			JobID:  req.JobID,
			Flags:  host.AttachFlagStdout | host.AttachFlagStderr | host.AttachFlagStdin | host.AttachFlagStream,
			Height: 20,
			Width:  10,
		})
		jobID = req.JobID
		piper, pipew := io.Pipe()
		go func() {
			stdin, err := ioutil.ReadAll(piper)
			c.Assert(err, IsNil)
			c.Assert(string(stdin), Equals, "test in")
			close(done)
		}()
		return &fakeAttachStream{strings.NewReader("test out"), pipew}, func() error { return nil }, nil
	})

	s.cc.SetHostClient(hostID, hc)
	s.cc.SetHosts(map[string]host.Host{hostID: host.Host{}})

	artifact := s.createTestArtifact(c, &ct.Artifact{Type: "docker", URI: "docker://foo/bar"})
	release := s.createTestRelease(c, &ct.Release{
		ArtifactID: artifact.ID,
		Env:        map[string]string{"RELEASE": "true", "FOO": "bar"},
	})

	data, _ := json.Marshal(&ct.NewJob{
		ReleaseID: release.ID,
		Cmd:       []string{"foo", "bar"},
		Env:       map[string]string{"JOB": "true", "FOO": "baz"},
		TTY:       true,
		Columns:   10,
		Lines:     20,
	})
	req, err := http.NewRequest("POST", s.srv.URL+"/apps/"+app.ID+"/jobs", bytes.NewBuffer(data))
	c.Assert(err, IsNil)
	req.SetBasicAuth("", authKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/vnd.flynn.attach")
	_, rwc, err := utils.HijackRequest(req, nil)
	c.Assert(err, IsNil)

	_, err = rwc.Write([]byte("test in"))
	c.Assert(err, IsNil)
	rwc.CloseWrite()
	stdout, err := ioutil.ReadAll(rwc)
	c.Assert(err, IsNil)
	c.Assert(string(stdout), Equals, "test out")
	rwc.Close()

	job := s.cc.GetHost(hostID).Jobs[0]
	c.Assert(job.ID, Equals, jobID)
	c.Assert(job.Attributes, DeepEquals, map[string]string{
		"flynn-controller.app":     app.ID,
		"flynn-controller.release": release.ID,
	})
	c.Assert(job.Config.Cmd, DeepEquals, []string{"foo", "bar"})
	sort.Strings(job.Config.Env)
	c.Assert(job.Config.Env, DeepEquals, []string{"FOO=baz", "JOB=true", "RELEASE=true"})
	c.Assert(job.Config.AttachStdout, Equals, true)
	c.Assert(job.Config.AttachStderr, Equals, true)
	c.Assert(job.Config.AttachStdin, Equals, true)
	c.Assert(job.Config.StdinOnce, Equals, true)
	c.Assert(job.Config.OpenStdin, Equals, true)
}
func (a *RunAppAction) Run(s *State) error {
	if a.AppStep != "" {
		data, err := getAppStep(s, a.AppStep)
		if err != nil {
			return err
		}
		a.App = data.App
		procs := a.Processes
		a.ExpandedFormation = data.ExpandedFormation
		a.Processes = procs
	}
	as := &RunAppState{
		ExpandedFormation: a.ExpandedFormation,
		Resources:         make([]*resource.Resource, 0, len(a.Resources)),
		Providers:         make([]*ct.Provider, 0, len(a.Resources)),
	}
	s.StepData[a.ID] = as

	if a.App == nil || a.App.ID == "" {
		a.App = &ct.App{ID: utils.UUID()}
	}
	if a.Artifact == nil {
		return errors.New("bootstrap: artifact must be set")
	}
	if a.Artifact.ID == "" {
		a.Artifact.ID = utils.UUID()
	}
	if a.Release == nil {
		return errors.New("bootstrap: release must be set")
	}
	if a.Release.ID == "" {
		a.Release.ID = utils.UUID()
	}
	a.Release.ArtifactID = a.Artifact.ID
	if a.Release.Env == nil {
		a.Release.Env = make(map[string]string)
	}
	interpolateRelease(s, a.Release)

	for _, p := range a.Resources {
		server, err := resource.NewServer(p.URL)
		if err != nil {
			return err
		}
		res, err := server.Provision(nil)
		server.Close()
		if err != nil {
			return err
		}
		as.Providers = append(as.Providers, p)
		as.Resources = append(as.Resources, res)
		for k, v := range res.Env {
			a.Release.Env[k] = v
		}
	}

	cc, err := s.ClusterClient()
	if err != nil {
		return err
	}
	hosts, err := cc.ListHosts()
	if err != nil {
		return err
	}
	hostIDs := make([]string, 0, len(hosts))
	for id := range hosts {
		hostIDs = append(hostIDs, id)
	}
	for typ, count := range a.Processes {
		for i := 0; i < count; i++ {
			config, err := utils.JobConfig(a.ExpandedFormation, typ)
			if err != nil {
				return err
			}
			job, err := startJob(s, hostIDs[i%len(hosts)], config)
			if err != nil {
				return err
			}
			as.Jobs = append(as.Jobs, *job)
		}
	}

	return nil
}
func (a *AddAppAction) Run(s *State) error {
	data, ok := s.StepData[a.FromStep].(*RunAppState)
	if !ok {
		return fmt.Errorf("bootstrap: unable to find step %q", a.FromStep)
	}
	as := &AppState{
		ExpandedFormation: &ct.ExpandedFormation{},
		Resources:         make([]*ct.Resource, 0, len(data.Resources)),
	}
	s.StepData[a.ID] = as

	client, err := s.ControllerClient()
	if err != nil {
		return err
	}

	a.App.ID = data.App.ID
	if err := client.CreateApp(a.App); err != nil {
		return err
	}
	as.App = a.App
	if err := client.CreateArtifact(data.Artifact); err != nil {
		return err
	}
	as.Artifact = data.Artifact
	if err := client.CreateRelease(data.Release); err != nil {
		return err
	}
	as.Release = data.Release

	for i, p := range data.Providers {
		if provider, ok := s.Providers[p.Name]; ok {
			p = provider
		} else {
			if err := client.CreateProvider(p); err != nil {
				return err
			}
			s.Providers[p.Name] = p
		}

		resource := &ct.Resource{
			ID:         utils.UUID(),
			ProviderID: p.ID,
			ExternalID: data.Resources[i].ID,
			Env:        data.Resources[i].Env,
		}
		if err := client.PutResource(resource); err != nil {
			return err
		}
		as.Resources = append(as.Resources, resource)
	}

	formation := &ct.Formation{
		AppID:     data.App.ID,
		ReleaseID: data.Release.ID,
		Processes: data.Processes,
	}
	if err := client.PutFormation(formation); err != nil {
		return err
	}
	as.Formation = formation
	if err := client.SetAppRelease(data.App.ID, data.Release.ID); err != nil {
		return err
	}

	return nil
}