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 }
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() }
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 }
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 }
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() }
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 }
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 }
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() }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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() }
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 }
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() }
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 }
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 }