예제 #1
0
func (r *ReleaseRepo) Add(data interface{}) error {
	release := data.(*ct.Release)
	releaseCopy := *release

	releaseCopy.ID = ""
	releaseCopy.ArtifactID = ""
	releaseCopy.CreatedAt = nil

	for typ, proc := range releaseCopy.Processes {
		resource.SetDefaults(&proc.Resources)
		releaseCopy.Processes[typ] = proc
	}

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

	var artifactID *string
	if release.ArtifactID != "" {
		artifactID = &release.ArtifactID
	}

	err = r.db.QueryRow("INSERT INTO releases (release_id, artifact_id, data) VALUES ($1, $2, $3) RETURNING created_at",
		release.ID, artifactID, data).Scan(&release.CreatedAt)

	release.ID = postgres.CleanUUID(release.ID)
	if release.ArtifactID != "" {
		release.ArtifactID = postgres.CleanUUID(release.ArtifactID)
	}
	return err
}
예제 #2
0
파일: release.go 프로젝트: supermario/flynn
func (r *ReleaseRepo) Add(data interface{}) error {
	release := data.(*ct.Release)
	releaseCopy := *release

	releaseCopy.ID = ""
	releaseCopy.ArtifactID = ""
	releaseCopy.CreatedAt = nil

	for typ, proc := range releaseCopy.Processes {
		resource.SetDefaults(&proc.Resources)
		releaseCopy.Processes[typ] = proc
	}

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

	var artifactID *string
	if release.ArtifactID != "" {
		artifactID = &release.ArtifactID
	}

	tx, err := r.db.Begin()
	if err != nil {
		return err
	}

	err = tx.QueryRow("INSERT INTO releases (release_id, artifact_id, data) VALUES ($1, $2, $3) RETURNING created_at",
		release.ID, artifactID, data).Scan(&release.CreatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}

	release.ID = postgres.CleanUUID(release.ID)
	if release.ArtifactID != "" {
		release.ArtifactID = postgres.CleanUUID(release.ArtifactID)
	}

	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   release.ID,
		ObjectType: ct.EventTypeRelease,
	}, release); err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}
예제 #3
0
파일: jobs.go 프로젝트: josephwinston/flynn
func scanJobEvent(s postgres.Scanner) (*ct.JobEvent, error) {
	event := &ct.JobEvent{}
	err := s.Scan(&event.ID, &event.JobID, &event.AppID, &event.ReleaseID, &event.Type, &event.State, &event.CreatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	event.AppID = postgres.CleanUUID(event.AppID)
	event.ReleaseID = postgres.CleanUUID(event.ReleaseID)
	return event, nil
}
예제 #4
0
func (r *DeploymentRepo) Add(data interface{}) error {
	d := data.(*ct.Deployment)
	if d.ID == "" {
		d.ID = random.UUID()
	}
	var oldReleaseID *string
	if d.OldReleaseID != "" {
		oldReleaseID = &d.OldReleaseID
	}
	procs := procsHstore(d.Processes)
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	query := "INSERT INTO deployments (deployment_id, app_id, old_release_id, new_release_id, strategy, processes) VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at"
	if err := tx.QueryRow(query, d.ID, d.AppID, oldReleaseID, d.NewReleaseID, d.Strategy, procs).Scan(&d.CreatedAt); err != nil {
		tx.Rollback()
		return err
	}
	d.ID = postgres.CleanUUID(d.ID)
	d.AppID = postgres.CleanUUID(d.AppID)
	d.OldReleaseID = postgres.CleanUUID(d.OldReleaseID)
	d.NewReleaseID = postgres.CleanUUID(d.NewReleaseID)

	// fake initial deployment
	if d.FinishedAt != nil {
		if _, err := tx.Exec("UPDATE deployments SET finished_at = $2 WHERE deployment_id = $1", d.ID, d.FinishedAt); err != nil {
			tx.Rollback()
			return err
		}
		return tx.Commit()
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	args, err := json.Marshal(ct.DeployID{ID: d.ID})
	if err != nil {
		return err
	}
	// TODO: wrap all of this in a transaction once we move to pgx
	if err := r.q.Enqueue(&que.Job{
		Type: "deployment",
		Args: args,
	}); err != nil {
		return err
	}
	return nil
}
예제 #5
0
파일: provider.go 프로젝트: suifing/flynn
func (r *ProviderRepo) Add(data interface{}) error {
	p := data.(*ct.Provider)
	if p.Name == "" {
		return errors.New("controller: name must not be blank")
	}
	if p.URL == "" {
		return errors.New("controler: url must not be blank")
	}
	// TODO: validate url
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	err = tx.QueryRow("INSERT INTO providers (name, url) VALUES ($1, $2) RETURNING provider_id, created_at, updated_at", p.Name, p.URL).Scan(&p.ID, &p.CreatedAt, &p.UpdatedAt)
	if err != nil {
		tx.Rollback()
		return err
	}
	p.ID = postgres.CleanUUID(p.ID)
	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   p.ID,
		ObjectType: ct.EventTypeProvider,
	}, p); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
예제 #6
0
func scanArtifact(s postgres.Scanner) (*ct.Artifact, error) {
	artifact := &ct.Artifact{}
	err := s.Scan(&artifact.ID, &artifact.Type, &artifact.URI, &artifact.CreatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	artifact.ID = postgres.CleanUUID(artifact.ID)
	return artifact, err
}
예제 #7
0
파일: provider.go 프로젝트: suifing/flynn
func scanProvider(s postgres.Scanner) (*ct.Provider, error) {
	p := &ct.Provider{}
	err := s.Scan(&p.ID, &p.Name, &p.URL, &p.CreatedAt, &p.UpdatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	p.ID = postgres.CleanUUID(p.ID)
	return p, err
}
예제 #8
0
파일: controller.go 프로젝트: suifing/flynn
func streamRouterEvents(rc routerc.Client, db *postgres.DB, doneCh chan struct{}) error {
	// wait for router to come up
	{
		events := make(chan *discoverd.Event)
		stream, err := discoverd.NewService("router-api").Watch(events)
		if err != nil {
			return err
		}
		for e := range events {
			if e.Kind == discoverd.EventKindUp {
				break
			}
		}
		stream.Close()
	}

	events := make(chan *router.StreamEvent)
	s, err := rc.StreamEvents(events)
	if err != nil {
		return err
	}
	go func() {
		for {
			e, ok := <-events
			if !ok {
				return
			}
			route := e.Route
			route.ID = postgres.CleanUUID(route.ID)
			var appID string
			if strings.HasPrefix(route.ParentRef, routeParentRefPrefix) {
				appID = strings.TrimPrefix(route.ParentRef, routeParentRefPrefix)
			}
			eventType := ct.EventTypeRoute
			if e.Event == "remove" {
				eventType = ct.EventTypeRouteDeletion
			}
			hash := md5.New()
			io.WriteString(hash, appID)
			io.WriteString(hash, string(eventType))
			io.WriteString(hash, route.ID)
			io.WriteString(hash, route.CreatedAt.String())
			io.WriteString(hash, route.UpdatedAt.String())
			uniqueID := fmt.Sprintf("%x", hash.Sum(nil))
			if err := createEvent(wrapDBExec(db.Exec), &ct.Event{
				AppID:      appID,
				ObjectID:   route.ID,
				ObjectType: eventType,
				UniqueID:   uniqueID,
			}, route); err != nil {
				log.Println(err)
			}
		}
	}()
	_, _ = <-doneCh
	return s.Close()
}
예제 #9
0
파일: app.go 프로젝트: suifing/flynn
func (r *AppRepo) Add(data interface{}) error {
	app := data.(*ct.App)
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	if app.Name == "" {
		var nameID uint32
		if err := tx.QueryRow("SELECT nextval('name_ids')").Scan(&nameID); err != nil {
			tx.Rollback()
			return err
		}
		app.Name = name.Get(nameID)
	}
	if len(app.Name) > 100 || !appNamePattern.MatchString(app.Name) {
		return ct.ValidationError{Field: "name", Message: "is invalid"}
	}
	if app.ID == "" {
		app.ID = random.UUID()
	}
	if app.Strategy == "" {
		app.Strategy = "all-at-once"
	}
	meta := metaToHstore(app.Meta)
	if err := tx.QueryRow("INSERT INTO apps (app_id, name, meta, strategy) VALUES ($1, $2, $3, $4) RETURNING created_at, updated_at", app.ID, app.Name, meta, app.Strategy).Scan(&app.CreatedAt, &app.UpdatedAt); err != nil {
		tx.Rollback()
		if postgres.IsUniquenessError(err, "apps_name_idx") {
			return httphelper.ObjectExistsErr(fmt.Sprintf("application %q already exists", app.Name))
		}
		return err
	}
	app.ID = postgres.CleanUUID(app.ID)

	if err := createEvent(tx.Exec, &ct.Event{
		AppID:      app.ID,
		ObjectID:   app.ID,
		ObjectType: ct.EventTypeApp,
	}, app); err != nil {
		tx.Rollback()
		return err
	}
	if err := tx.Commit(); err != nil {
		return err
	}

	if !app.System() && r.defaultDomain != "" {
		route := (&router.HTTPRoute{
			Domain:  fmt.Sprintf("%s.%s", app.Name, r.defaultDomain),
			Service: app.Name + "-web",
		}).ToRoute()
		if err := createRoute(r.db, r.router, app.ID, route); err != nil {
			log.Printf("Error creating default route for %s: %s", app.Name, err)
		}
	}
	return nil
}
예제 #10
0
파일: release.go 프로젝트: supermario/flynn
func scanRelease(s postgres.Scanner) (*ct.Release, error) {
	var artifactID *string
	release := &ct.Release{}
	var data []byte
	err := s.Scan(&release.ID, &artifactID, &data, &release.CreatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	if artifactID != nil {
		release.ArtifactID = *artifactID
	}
	release.ID = postgres.CleanUUID(release.ID)
	release.ArtifactID = postgres.CleanUUID(release.ArtifactID)
	err = json.Unmarshal(data, release)
	return release, err
}
예제 #11
0
파일: jobs.go 프로젝트: josephwinston/flynn
func scanJob(s postgres.Scanner) (*ct.Job, error) {
	job := &ct.Job{}
	var meta hstore.Hstore
	err := s.Scan(&job.ID, &job.AppID, &job.ReleaseID, &job.Type, &job.State, &meta, &job.CreatedAt, &job.UpdatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	if len(meta.Map) > 0 {
		job.Meta = make(map[string]string, len(meta.Map))
		for k, v := range meta.Map {
			job.Meta[k] = v.String
		}
	}
	job.AppID = postgres.CleanUUID(job.AppID)
	job.ReleaseID = postgres.CleanUUID(job.ReleaseID)
	return job, nil
}
예제 #12
0
func scanDeployment(s postgres.Scanner) (*ct.Deployment, error) {
	d := &ct.Deployment{}
	var procs hstore.Hstore
	err := s.Scan(&d.ID, &d.AppID, &d.OldReleaseID, &d.NewReleaseID, &d.Strategy, &procs, &d.CreatedAt, &d.FinishedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	d.Processes = make(map[string]int, len(procs.Map))
	for k, v := range procs.Map {
		n, _ := strconv.Atoi(v.String)
		if n > 0 {
			d.Processes[k] = n
		}
	}
	d.ID = postgres.CleanUUID(d.ID)
	d.AppID = postgres.CleanUUID(d.AppID)
	d.OldReleaseID = postgres.CleanUUID(d.OldReleaseID)
	d.NewReleaseID = postgres.CleanUUID(d.NewReleaseID)
	return d, err
}
예제 #13
0
파일: app.go 프로젝트: suifing/flynn
func scanApp(s postgres.Scanner) (*ct.App, error) {
	app := &ct.App{}
	var meta hstore.Hstore
	var releaseID *string
	err := s.Scan(&app.ID, &app.Name, &meta, &app.Strategy, &releaseID, &app.CreatedAt, &app.UpdatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	if releaseID != nil {
		app.ReleaseID = postgres.CleanUUID(*releaseID)
	}
	if len(meta.Map) > 0 {
		app.Meta = make(map[string]string, len(meta.Map))
		for k, v := range meta.Map {
			app.Meta[k] = v.String
		}
	}
	app.ID = postgres.CleanUUID(app.ID)
	return app, err
}
예제 #14
0
func scanFormation(s postgres.Scanner) (*ct.Formation, error) {
	f := &ct.Formation{}
	var procs hstore.Hstore
	err := s.Scan(&f.AppID, &f.ReleaseID, &procs, &f.CreatedAt, &f.UpdatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	f.Processes = make(map[string]int, len(procs.Map))
	for k, v := range procs.Map {
		n, _ := strconv.Atoi(v.String)
		if n > 0 {
			f.Processes[k] = n
		}
	}
	f.AppID = postgres.CleanUUID(f.AppID)
	f.ReleaseID = postgres.CleanUUID(f.ReleaseID)
	return f, nil
}
예제 #15
0
func (r *ProviderRepo) Add(data interface{}) error {
	p := data.(*ct.Provider)
	if p.Name == "" {
		return errors.New("controller: name must not be blank")
	}
	if p.URL == "" {
		return errors.New("controler: url must not be blank")
	}
	// TODO: validate url
	err := r.db.QueryRow("INSERT INTO providers (name, url) VALUES ($1, $2) RETURNING provider_id, created_at, updated_at", p.Name, p.URL).Scan(&p.ID, &p.CreatedAt, &p.UpdatedAt)
	p.ID = postgres.CleanUUID(p.ID)
	return err
}
예제 #16
0
파일: resource.go 프로젝트: suifing/flynn
func scanResource(s postgres.Scanner) (*ct.Resource, error) {
	r := &ct.Resource{}
	var env hstore.Hstore
	var appIDs string
	err := s.Scan(&r.ID, &r.ProviderID, &r.ExternalID, &env, &appIDs, &r.CreatedAt)
	if err == sql.ErrNoRows {
		err = ErrNotFound
	}
	r.ID = postgres.CleanUUID(r.ID)
	r.ProviderID = postgres.CleanUUID(r.ProviderID)
	r.Env = make(map[string]string, len(env.Map))
	for k, v := range env.Map {
		r.Env[k] = v.String
	}
	if appIDs != "" {
		r.Apps = split(appIDs[1:len(appIDs)-1], ",")
	}
	for i, id := range r.Apps {
		r.Apps[i] = postgres.CleanUUID(id)
	}
	return r, err
}
예제 #17
0
func (rr *ResourceRepo) Add(r *ct.Resource) error {
	if r.ID == "" {
		r.ID = random.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] = postgres.CleanUUID(r.Apps[i])
	}
	r.ID = postgres.CleanUUID(r.ID)
	return tx.Commit()
}
예제 #18
0
파일: app.go 프로젝트: NeilW/flynn
func scanAppEvent(s postgres.Scanner) (*ct.AppEvent, error) {
	var event ct.AppEvent
	var typ string
	var data []byte
	err := s.Scan(&event.ID, &event.AppID, &event.ObjectID, &typ, &data, &event.CreatedAt)
	if err != nil {
		if err == sql.ErrNoRows {
			err = ErrNotFound
		}
		return nil, err
	}
	event.AppID = postgres.CleanUUID(event.AppID)
	event.ObjectType = ct.EventType(typ)
	event.Data = json.RawMessage(data)
	return &event, nil
}
예제 #19
0
func (r *ArtifactRepo) Add(data interface{}) error {
	a := data.(*ct.Artifact)
	// TODO: actually validate
	if a.ID == "" {
		a.ID = random.UUID()
	}
	if a.Type == "" {
		return ct.ValidationError{"type", "must not be empty"}
	}
	if a.URI == "" {
		return ct.ValidationError{"uri", "must not be empty"}
	}
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	err = tx.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 postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = r.db.Begin()
		if err != nil {
			return err
		}
		err = tx.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 {
			tx.Rollback()
			return err
		}
	} else if err == nil {
		a.ID = postgres.CleanUUID(a.ID)
		if err := createEvent(tx.Exec, &ct.Event{
			ObjectID:   a.ID,
			ObjectType: ct.EventTypeArtifact,
		}, a); err != nil {
			tx.Rollback()
			return err
		}
	}
	if err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
예제 #20
0
func (r *ArtifactRepo) Add(data interface{}) error {
	a := data.(*ct.Artifact)
	// TODO: actually validate
	if a.ID == "" {
		a.ID = random.UUID()
	}
	if a.Type == "" {
		return ct.ValidationError{"type", "must not be empty"}
	}
	if a.URI == "" {
		return ct.ValidationError{"uri", "must not be empty"}
	}
	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 postgres.IsUniquenessError(err, "") {
		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 = postgres.CleanUUID(a.ID)
	return err
}
예제 #21
0
func (r *FormationRepo) expandFormation(formation *ct.Formation) (*ct.ExpandedFormation, error) {
	app, err := r.apps.Get(formation.AppID)
	if err == ErrNotFound {
		app = &ct.App{ID: postgres.CleanUUID(formation.AppID)}
	} else if err != nil {
		return nil, err
	}
	release, err := r.releases.Get(formation.ReleaseID)
	if err != nil {
		return nil, err
	}
	artifact, err := r.artifacts.Get(release.(*ct.Release).ArtifactID)
	if err != nil {
		return nil, err
	}
	f := &ct.ExpandedFormation{
		App:       app.(*ct.App),
		Release:   release.(*ct.Release),
		Artifact:  artifact.(*ct.Artifact),
		Processes: formation.Processes,
		UpdatedAt: *formation.UpdatedAt,
	}
	return f, nil
}