示例#1
0
文件: jobs.go 项目: qiukeren/flynn
func (r *JobRepo) Add(job *ct.Job) error {
	meta, err := json.Marshal(job.Meta)
	if err != nil {
		return err
	}
	// TODO: actually validate
	err = r.db.QueryRow("INSERT INTO job_cache (job_id, app_id, release_id, process_type, state, meta) VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at, updated_at",
		job.ID, job.AppID, job.ReleaseID, job.Type, job.State, meta).Scan(&job.CreatedAt, &job.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		err = r.db.QueryRow("UPDATE job_cache SET state = $2, updated_at = now() WHERE job_id = $1 RETURNING created_at, updated_at",
			job.ID, job.State).Scan(&job.CreatedAt, &job.UpdatedAt)
		if e, ok := err.(*pq.Error); ok && e.Code.Name() == "check_violation" {
			return ct.ValidationError{Field: "state", Message: e.Error()}
		}
	}
	if err != nil {
		return err
	}

	// create a job event, ignoring possible duplications
	uniqueID := strings.Join([]string{job.ID, job.State}, "|")
	data, err := json.Marshal(job)
	if err != nil {
		return err
	}
	err = r.db.Exec("INSERT INTO events (app_id, object_id, unique_id, object_type, data) VALUES ($1, $2, $3, $4, $5)", job.AppID, job.ID, uniqueID, string(ct.EventTypeJob), data)
	if postgres.IsUniquenessError(err, "") {
		return nil
	}
	return err
}
示例#2
0
func (r *JobRepo) Add(job *ct.Job) error {
	hostID, jobID, err := cluster.ParseJobID(job.ID)
	if err != nil {
		log.Printf("Unable to parse hostID from %q", job.ID)
		return ErrNotFound
	}
	meta := metaToHstore(job.Meta)
	// TODO: actually validate
	err = r.db.QueryRow("INSERT INTO job_cache (job_id, host_id, app_id, release_id, process_type, state, meta) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING created_at, updated_at",
		jobID, hostID, job.AppID, job.ReleaseID, job.Type, job.State, meta).Scan(&job.CreatedAt, &job.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		err = r.db.QueryRow("UPDATE job_cache SET state = $3, updated_at = now() WHERE job_id = $1 AND host_id = $2 RETURNING created_at, updated_at",
			jobID, hostID, job.State).Scan(&job.CreatedAt, &job.UpdatedAt)
		if e, ok := err.(*pq.Error); ok && e.Code.Name() == "check_violation" {
			return ct.ValidationError{Field: "state", Message: e.Error()}
		}
	}
	if err != nil {
		return err
	}

	// create a job event, ignoring possible duplications
	err = r.db.Exec("INSERT INTO job_events (job_id, host_id, app_id, state) VALUES ($1, $2, $3, $4)", jobID, hostID, job.AppID, job.State)
	if postgres.IsUniquenessError(err, "") {
		return nil
	}
	return err
}
示例#3
0
func (r *FormationRepo) Add(f *ct.Formation) error {
	if err := r.validateFormProcs(f); err != nil {
		return err
	}
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	procs := procsHstore(f.Processes)
	err = tx.QueryRow("INSERT INTO formations (app_id, release_id, processes) VALUES ($1, $2, $3) RETURNING created_at, updated_at",
		f.AppID, f.ReleaseID, procs).Scan(&f.CreatedAt, &f.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = r.db.Begin()
		if err != nil {
			return err
		}
		err = tx.QueryRow("UPDATE formations SET processes = $3, updated_at = now(), deleted_at = NULL WHERE app_id = $1 AND release_id = $2 RETURNING created_at, updated_at",
			f.AppID, f.ReleaseID, procs).Scan(&f.CreatedAt, &f.UpdatedAt)
	}
	if err != nil {
		tx.Rollback()
		return err
	}
	if err := createEvent(tx.Exec, &ct.Event{
		AppID:      f.AppID,
		ObjectID:   f.AppID + ":" + f.ReleaseID,
		ObjectType: ct.EventTypeScale,
	}, f.Processes); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
示例#4
0
文件: artifact.go 项目: imjorge/flynn
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{Field: "type", Message: "must not be empty"}
	}
	if a.URI == "" {
		return ct.ValidationError{Field: "uri", Message: "must not be empty"}
	}
	if a.Type == ct.ArtifactTypeFlynn && a.RawManifest == nil {
		if a.Size <= 0 {
			return ct.ValidationError{Field: "size", Message: "must be greater than zero"}
		}
		if err := downloadManifest(a); err != nil {
			return ct.ValidationError{Field: "manifest", Message: fmt.Sprintf("failed to download from %s: %s", a.URI, err)}
		}
	}

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

	err = tx.QueryRow("artifact_insert", a.ID, string(a.Type), a.URI, a.Meta, []byte(a.RawManifest), a.Hashes, a.Size, a.LayerURLTemplate).Scan(&a.CreatedAt)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = r.db.Begin()
		if err != nil {
			return err
		}
		var size *int64
		var layerURLTemplate *string
		err = tx.QueryRow("artifact_select_by_type_and_uri", string(a.Type), a.URI).Scan(&a.ID, &a.Meta, &a.RawManifest, &a.Hashes, &size, &layerURLTemplate, &a.CreatedAt)
		if err != nil {
			tx.Rollback()
			return err
		}
		if size != nil {
			a.Size = *size
		}
		if layerURLTemplate != nil {
			a.LayerURLTemplate = *layerURLTemplate
		}
	}
	if err != nil {
		tx.Rollback()
		return err
	}
	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   a.ID,
		ObjectType: ct.EventTypeArtifact,
	}, a); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
示例#5
0
func (d *pgDataStore) Add(r *router.Route) (err error) {
	switch d.tableName {
	case tableNameHTTP:
		err = d.pgx.QueryRow(
			sqlAddRouteHTTP,
			r.ParentRef,
			r.Service,
			r.Leader,
			r.Domain,
			r.TLSCert,
			r.TLSKey,
			r.Sticky,
			r.Path,
		).Scan(&r.ID, &r.CreatedAt, &r.UpdatedAt)
	case tableNameTCP:
		err = d.pgx.QueryRow(
			sqlAddRouteTCP,
			r.ParentRef,
			r.Service,
			r.Leader,
			r.Port,
		).Scan(&r.ID, &r.CreatedAt, &r.UpdatedAt)
	}
	r.Type = d.routeType
	if postgres.IsUniquenessError(err, "") {
		err = ErrConflict
	} else if postgres.IsPostgresCode(err, postgres.RaiseException) {
		err = ErrInvalid
	}
	return err
}
示例#6
0
文件: app.go 项目: qiukeren/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 || !utils.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, err := json.Marshal(app.Meta)
	if err != nil {
		return err
	}
	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
	}

	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
}
示例#7
0
文件: app.go 项目: BobbWu/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 int64
		if err := tx.QueryRow("app_next_name_id").Scan(&nameID); err != nil {
			tx.Rollback()
			return err
		}
		// Safe cast because name_ids is limited to 32 bit size in schema
		app.Name = name.Get(uint32(nameID))
	}
	if len(app.Name) > 100 || !utils.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"
	}
	if app.DeployTimeout == 0 {
		app.DeployTimeout = ct.DefaultDeployTimeout
	}
	if err := tx.QueryRow("app_insert", app.ID, app.Name, app.Meta, app.Strategy, app.DeployTimeout).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
	}

	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
}
示例#8
0
func (r *JobRepo) Add(job *ct.Job) error {
	// TODO: actually validate
	err := r.db.QueryRow(
		"job_insert",
		job.ID,
		job.UUID,
		job.HostID,
		job.AppID,
		job.ReleaseID,
		job.Type,
		string(job.State),
		job.Meta,
		job.ExitStatus,
		job.HostError,
		job.RunAt,
		job.Restarts,
	).Scan(&job.CreatedAt, &job.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		err = r.db.QueryRow(
			"job_update",
			job.UUID,
			job.ID,
			job.HostID,
			string(job.State),
			job.ExitStatus,
			job.HostError,
			job.RunAt,
			job.Restarts,
		).Scan(&job.CreatedAt, &job.UpdatedAt)
		if postgres.IsPostgresCode(err, postgres.CheckViolation) {
			return ct.ValidationError{Field: "state", Message: err.Error()}
		}
	}
	if err != nil {
		return err
	}

	// create a job event, ignoring possible duplications
	uniqueID := strings.Join([]string{job.UUID, string(job.State)}, "|")
	err = r.db.Exec("event_insert_unique", job.AppID, job.UUID, uniqueID, string(ct.EventTypeJob), job)
	if postgres.IsUniquenessError(err, "") {
		return nil
	}
	return err
}
示例#9
0
func (p *PostgresFilesystem) Put(name string, r io.Reader, typ string) error {
	tx, err := p.db.Begin()
	if err != nil {
		return err
	}

	var id oid.Oid
create:
	err = tx.QueryRow("INSERT INTO files (name, type) VALUES ($1, $2) RETURNING file_id", name, typ).Scan(&id)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = p.db.Begin()
		if err != nil {
			return err
		}

		// file exists, delete it first
		_, err = tx.Exec("DELETE FROM files WHERE name = $1", name)
		if err != nil {
			tx.Rollback()
			return err
		}
		goto create
	}
	if err != nil {
		tx.Rollback()
		return err
	}

	lo, err := pq.NewLargeObjects(tx)
	if err != nil {
		tx.Rollback()
		return err
	}
	obj, err := lo.Open(id, pq.LargeObjectModeWrite)
	if err != nil {
		tx.Rollback()
		return err
	}

	h := sha512.New()
	size, err := io.Copy(obj, io.TeeReader(r, h))
	if err != nil {
		tx.Rollback()
		return err
	}

	digest := hex.EncodeToString(h.Sum(nil))
	_, err = tx.Exec("UPDATE files SET size = $2, digest = $3 WHERE file_id = $1", id, size, digest)
	if err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}
示例#10
0
func (r *FormationRepo) Add(f *ct.Formation) error {
	if err := r.validateFormProcs(f); err != nil {
		return err
	}
	procs := procsHstore(f.Processes)
	err := r.db.QueryRow("INSERT INTO formations (app_id, release_id, processes) VALUES ($1, $2, $3) RETURNING created_at, updated_at",
		f.AppID, f.ReleaseID, procs).Scan(&f.CreatedAt, &f.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		err = r.db.QueryRow("UPDATE formations SET processes = $3, updated_at = now(), deleted_at = NULL WHERE app_id = $1 AND release_id = $2 RETURNING created_at, updated_at",
			f.AppID, f.ReleaseID, procs).Scan(&f.CreatedAt, &f.UpdatedAt)
	}
	if err != nil {
		return err
	}
	return nil
}
示例#11
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()
}
示例#12
0
文件: artifact.go 项目: devick/flynn
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("artifact_insert",
		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("artifact_select_by_type_and_uri",
			a.Type, a.URI).Scan(&a.ID, &a.CreatedAt)
		if err != nil {
			tx.Rollback()
			return err
		}
	} else if err == nil {
		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()
}
示例#13
0
func (d *pgDataStore) Add(r *router.Route) (err error) {
	switch d.tableName {
	case tableNameHTTP:
		err = d.addHTTP(r)
	case tableNameTCP:
		err = d.addTCP(r)
	}
	r.Type = d.routeType
	if err != nil {
		if postgres.IsUniquenessError(err, "") {
			err = ErrConflict
		} else if postgres.IsPostgresCode(err, postgres.RaiseException) {
			err = ErrInvalid
		}
		return err
	}
	return nil
}
示例#14
0
文件: formation.go 项目: devick/flynn
func (r *FormationRepo) Add(f *ct.Formation) error {
	if err := r.validateFormProcs(f); err != nil {
		return err
	}
	scale := &ct.Scale{
		Processes: f.Processes,
		ReleaseID: f.ReleaseID,
	}
	prevFormation, _ := r.Get(f.AppID, f.ReleaseID)
	if prevFormation != nil {
		scale.PrevProcesses = prevFormation.Processes
	}
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}
	err = tx.QueryRow("formation_insert", f.AppID, f.ReleaseID, f.Processes, f.Tags).Scan(&f.CreatedAt, &f.UpdatedAt)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = r.db.Begin()
		if err != nil {
			return err
		}
		err = tx.QueryRow("formation_update", f.AppID, f.ReleaseID, f.Processes, f.Tags).Scan(&f.CreatedAt, &f.UpdatedAt)
	}
	if err != nil {
		tx.Rollback()
		return err
	}
	if err := createEvent(tx.Exec, &ct.Event{
		AppID:      f.AppID,
		ObjectID:   f.AppID + ":" + f.ReleaseID,
		ObjectType: ct.EventTypeScale,
	}, scale); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
示例#15
0
文件: key.go 项目: suifing/flynn
func (r *KeyRepo) Add(data interface{}) error {
	key := data.(*ct.Key)

	if key.Key == "" {
		return errors.New("controller: key must not be blank")
	}

	pubKey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Key))
	if err != nil {
		return err
	}

	key.ID = fingerprintKey(pubKey.Marshal())
	key.Key = string(bytes.TrimSpace(ssh.MarshalAuthorizedKey(pubKey)))
	key.Comment = comment

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

	err = tx.QueryRow("INSERT INTO keys (fingerprint, key, comment) VALUES ($1, $2, $3) RETURNING created_at", key.ID, key.Key, key.Comment).Scan(&key.CreatedAt)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		return nil
	}
	if err != nil {
		tx.Rollback()
		return err
	}
	if err := createEvent(tx.Exec, &ct.Event{
		ObjectID:   key.ID,
		ObjectType: ct.EventTypeKey,
	}, key); err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit()
}
示例#16
0
文件: app.go 项目: NeilW/flynn
func (r *AppRepo) Add(data interface{}) error {
	app := data.(*ct.App)
	if app.Name == "" {
		var nameID uint32
		if err := r.db.QueryRow("SELECT nextval('name_ids')").Scan(&nameID); err != nil {
			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 := r.db.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 {
		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 !app.System() && r.defaultDomain != "" {
		route := (&router.HTTPRoute{
			Domain:  fmt.Sprintf("%s.%s", app.Name, r.defaultDomain),
			Service: app.Name + "-web",
		}).ToRoute()
		route.ParentRef = routeParentRef(app.ID)
		if err := r.router.CreateRoute(route); err != nil {
			log.Printf("Error creating default route for %s: %s", app.Name, err)
		}
	}
	return nil
}
示例#17
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
}
示例#18
0
func (r *FileRepo) Put(name string, data io.Reader, offset int64, typ string) error {
	tx, err := r.db.Begin()
	if err != nil {
		return err
	}

	info := backend.FileInfo{
		Name: name,
		Type: typ,
	}
	h := sha512.New().(resumable.Hash)
	b := r.defaultBackend

create:
	err = tx.QueryRow("INSERT INTO files (name, backend, type) VALUES ($1, $2, $3) RETURNING file_id", name, b.Name(), typ).Scan(&info.ID)
	if postgres.IsUniquenessError(err, "") {
		tx.Rollback()
		tx, err = r.db.Begin()
		if err != nil {
			return err
		}

		if offset > 0 {
			var backendName string
			var sha512State []byte
			var externalID *string
			// file exists, get details
			if err := tx.QueryRow(
				"SELECT file_id, file_oid, external_id, backend, size, sha512_state FROM files WHERE name = $1 AND deleted_at IS NULL", name,
			).Scan(&info.ID, &info.Oid, &externalID, &backendName, &info.Size, &sha512State); err != nil {
				tx.Rollback()
				return err
			}
			if externalID != nil {
				info.ExternalID = *externalID
			}
			b, err = r.getBackend(backendName)
			if err != nil {
				tx.Rollback()
				return err
			}
			if offset != info.Size {
				tx.Rollback()
				// TODO: pass error via HTTP response
				return fmt.Errorf("blobstore: offset (%d) does not match blob size (%d), unable to append", offset, info.Size)
			}
			if len(sha512State) > 0 {
				err = h.Restore(sha512State)
			}
			if (len(sha512State) == 0 || err != nil || h.Len() != info.Size) && info.Size > 0 {
				// hash state is not resumable, read current data into hash
				f, err := b.Open(tx, info, false)
				if err != nil {
					tx.Rollback()
					return err
				}
				h.Reset()
				if _, err := io.Copy(h, io.LimitReader(f, info.Size)); err != nil {
					f.Close()
					tx.Rollback()
					return err
				}
				f.Close()
			}
		} else {
			// file exists, not appending, overwrite by deleting
			var di backend.FileInfo
			var externalID *string
			var backendName string
			if err := tx.QueryRow(
				"UPDATE files SET deleted_at = now() WHERE name = $1 AND deleted_at IS NULL RETURNING file_id, external_id, backend", name,
			).Scan(&di.ID, &externalID, &backendName); err != nil {
				tx.Rollback()
				return err
			}
			if externalID != nil {
				di.ExternalID = *externalID
			}
			// delete old file from backend
			if err := func() error {
				if backendName == "postgres" {
					// no need to call delete, it is done automatically by a trigger
					return nil
				}
				b, err := r.getBackend(backendName)
				if err != nil {
					return err
				}
				return b.Delete(nil, di)
			}(); err != nil {
				log.Printf("Error deleting %s (%s) from backend %s: %s", di.ExternalID, name, backendName, err)
			}
			goto create
		}
	} else if err != nil {
		tx.Rollback()
		return err
	}

	sr := newSizeReader(data)
	sr.size = info.Size
	if err := b.Put(tx, info, io.TeeReader(sr, h), offset > 0); err != nil {
		tx.Rollback()
		return err
	}

	sha512State, _ := h.State()
	if err := tx.Exec(
		"UPDATE files SET size = $2, sha512 = $3, sha512_state = $4, updated_at = now() WHERE file_id = $1",
		info.ID, sr.Size(), h.Sum(nil), sha512State,
	); err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}
示例#19
0
func (c *controllerAPI) CreateDeployment(ctx context.Context, w http.ResponseWriter, req *http.Request) {
	var rid releaseID
	if err := httphelper.DecodeJSON(req, &rid); err != nil {
		respondWithError(w, err)
		return
	}

	rel, err := c.releaseRepo.Get(rid.ID)
	if err != nil {
		if err == ErrNotFound {
			err = ct.ValidationError{
				Message: fmt.Sprintf("could not find release with ID %s", rid.ID),
			}
		}
		respondWithError(w, err)
		return
	}
	release := rel.(*ct.Release)
	app := c.getApp(ctx)

	// TODO: wrap all of this in a transaction
	oldRelease, err := c.appRepo.GetRelease(app.ID)
	if err == ErrNotFound {
		oldRelease = &ct.Release{}
	} else if err != nil {
		respondWithError(w, err)
		return
	}
	oldFormation, err := c.formationRepo.Get(app.ID, oldRelease.ID)
	if err == ErrNotFound {
		oldFormation = &ct.Formation{}
	} else if err != nil {
		respondWithError(w, err)
		return
	}
	procCount := 0
	for _, i := range oldFormation.Processes {
		procCount += i
	}

	deployment := &ct.Deployment{
		AppID:         app.ID,
		NewReleaseID:  release.ID,
		Strategy:      app.Strategy,
		OldReleaseID:  oldRelease.ID,
		Processes:     oldFormation.Processes,
		DeployTimeout: app.DeployTimeout,
	}

	if err := schema.Validate(deployment); err != nil {
		respondWithError(w, err)
		return
	}
	if procCount == 0 {
		// immediately set app release
		if err := c.appRepo.SetRelease(app, release.ID); err != nil {
			respondWithError(w, err)
			return
		}
		now := time.Now()
		deployment.FinishedAt = &now
	}

	d, err := c.deploymentRepo.Add(deployment)
	if err != nil {
		if postgres.IsUniquenessError(err, "isolate_deploys") {
			httphelper.ValidationError(w, "", "Cannot create deploy, there is already one in progress for this app.")
			return
		}
		respondWithError(w, err)
		return
	}

	httphelper.JSON(w, 200, d)
}