Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove store #713

Merged
merged 1 commit into from
Jan 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* Allow floating point numbers to be provided when scaling the memory on a process [#694](https://github.com/remind101/empire/pull/694).
* Empire will now update the SSL certificate on the associated ELB if it changes from `emp cert-attach` [#700](https://github.com/remind101/empire/pull/700).
* The Tugboat integration now updates the deployment status with any errors that occurred [#709](https://github.com/remind101/empire/pull/709).
* Deploying a non-existent docker image to Empire will no longer create an app [#713](https://github.com/remind101/empire/pull/713).
* It's no longer necessary to re-deploy an application when scaling a process with new CPU or memory constraints [#713](https://github.com/remind101/empire/pull/713).

**Security**

Expand Down
179 changes: 81 additions & 98 deletions apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,35 +104,6 @@ func (q AppsQuery) Scope(db *gorm.DB) *gorm.DB {
return scope.Scope(db)
}

// AppsFind returns the first matching release.
func (s *store) AppsFind(scope Scope) (*App, error) {
var app App
return &app, s.First(scope, &app)
}

// Apps returns all apps matching the scope.
func (s *store) Apps(scope Scope) ([]*App, error) {
var apps []*App
// Default to ordering by name.
scope = ComposedScope{Order("name"), scope}
return apps, s.Find(scope, &apps)
}

// AppsCreate persists an app.
func (s *store) AppsCreate(app *App) (*App, error) {
return appsCreate(s.db, app)
}

// AppsUpdate updates an app.
func (s *store) AppsUpdate(app *App) error {
return appsUpdate(s.db, app)
}

// AppsDestroy destroys an app.
func (s *store) AppsDestroy(app *App) error {
return appsDestroy(s.db, app)
}

// AppID returns a scope to find an app by id.
func AppID(id string) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
Expand All @@ -144,71 +115,27 @@ type appsService struct {
*Empire
}

func (s *appsService) AppsDestroy(ctx context.Context, app *App) error {
if err := s.Scheduler.Remove(ctx, app.ID); err != nil {
// Destroy destroys removes an app from the scheduler, then destroys it here.
func (s *appsService) Destroy(ctx context.Context, db *gorm.DB, app *App) error {
if err := appsDestroy(db, app); err != nil {
return err
}

return s.store.AppsDestroy(app)
return s.Scheduler.Remove(ctx, app.ID)
}

// AppsEnsureRepo will set the repo if it's not set.
func (s *appsService) AppsEnsureRepo(app *App, repo string) error {
if app.Repo != nil {
return nil
}

app.Repo = &repo

return s.store.AppsUpdate(app)
}

// AppsFindOrCreateByRepo first attempts to find an app by repo, falling back to
// creating a new app.
func (s *appsService) AppsFindOrCreateByRepo(repo string) (*App, error) {
n := AppNameFromRepo(repo)
a, err := s.store.AppsFind(AppsQuery{Name: &n})
if err != nil && err != gorm.RecordNotFound {
return a, err
}

// If the app wasn't found, create a new app.
if err != gorm.RecordNotFound {
return a, s.AppsEnsureRepo(a, repo)
}

a = &App{
Name: n,
Repo: &repo,
func (s *appsService) Restart(ctx context.Context, db *gorm.DB, opts RestartOpts) error {
if opts.PID != "" {
return s.Scheduler.Stop(ctx, opts.PID)
}

return s.store.AppsCreate(a)
}

// AppsCreate inserts the app into the database.
func appsCreate(db *gorm.DB, app *App) (*App, error) {
return app, db.Create(app).Error
}

// AppsUpdate updates an app.
func appsUpdate(db *gorm.DB, app *App) error {
return db.Save(app).Error
}

// AppsDestroy destroys an app.
func appsDestroy(db *gorm.DB, app *App) error {
return db.Delete(app).Error
}

// scaler is a small service for scaling an apps process.
type scaler struct {
*Empire
return s.releases.ReleaseApp(ctx, db, opts.App)
}

func (s *scaler) Scale(ctx context.Context, opts ScaleOpts) (*Process, error) {
func (s *appsService) Scale(ctx context.Context, db *gorm.DB, opts ScaleOpts) (*Process, error) {
app, t, quantity, c := opts.App, opts.Process, opts.Quantity, opts.Constraints

release, err := s.store.ReleasesFind(ReleasesQuery{App: app})
release, err := releasesFind(db, ReleasesQuery{App: app})
if err != nil {
return nil, err
}
Expand All @@ -217,13 +144,8 @@ func (s *scaler) Scale(ctx context.Context, opts ScaleOpts) (*Process, error) {
return nil, &ValidationError{Err: fmt.Errorf("no releases for %s", app.Name)}
}

f, err := s.store.Formation(ProcessesQuery{Release: release})
if err != nil {
return nil, err
}

p, ok := f[t]
if !ok {
p := release.Process(t)
if p == nil {
return nil, &ValidationError{Err: fmt.Errorf("no %s process type in release", t)}
}

Expand All @@ -240,22 +162,83 @@ func (s *scaler) Scale(ctx context.Context, opts ScaleOpts) (*Process, error) {
p.Constraints = *c
}

if err := s.store.ProcessesUpdate(p); err != nil {
if err := processesUpdate(db, p); err != nil {
return nil, err
}

// If there are no changes to the process size, we can do a quick scale
// up, otherwise, we will resubmit the release to the scheduler.
if c == nil {
err = s.Scheduler.Scale(ctx, release.AppID, string(p.Type), uint(quantity))
} else {
err = s.releases.Release(ctx, release)
}

if err != nil {
return p, err
}

return p, s.PublishEvent(event)
}

// restarter is a small service for restarting an apps processes.
type restarter struct {
*Empire
// appsEnsureRepo will set the repo if it's not set.
func appsEnsureRepo(db *gorm.DB, app *App, repo string) error {
if app.Repo != nil {
return nil
}

app.Repo = &repo

return appsUpdate(db, app)
}

func (s *restarter) Restart(ctx context.Context, opts RestartOpts) error {
if opts.PID != "" {
return s.Scheduler.Stop(ctx, opts.PID)
// appsFindOrCreateByRepo first attempts to find an app by repo, falling back to
// creating a new app.
func appsFindOrCreateByRepo(db *gorm.DB, repo string) (*App, error) {
n := AppNameFromRepo(repo)
a, err := appsFind(db, AppsQuery{Name: &n})
if err != nil && err != gorm.RecordNotFound {
return a, err
}

// If the app wasn't found, create a new app.
if err != gorm.RecordNotFound {
return a, appsEnsureRepo(db, a, repo)
}

return s.releaser.ReleaseApp(ctx, opts.App)
a = &App{
Name: n,
Repo: &repo,
}

return appsCreate(db, a)
}

// appsFind finds a single app given the scope.
func appsFind(db *gorm.DB, scope Scope) (*App, error) {
var app App
return &app, first(db, scope, &app)
}

// apps finds all apps matching the scope.
func apps(db *gorm.DB, scope Scope) ([]*App, error) {
var apps []*App
// Default to ordering by name.
scope = ComposedScope{Order("name"), scope}
return apps, find(db, scope, &apps)
}

// appsCreate inserts the app into the database.
func appsCreate(db *gorm.DB, app *App) (*App, error) {
return app, db.Create(app).Error
}

// appsUpdate updates an app.
func appsUpdate(db *gorm.DB, app *App) error {
return db.Save(app).Error
}

// appsDestroy destroys an app.
func appsDestroy(db *gorm.DB, app *App) error {
return db.Delete(app).Error
}
11 changes: 7 additions & 4 deletions certs.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package empire

import "golang.org/x/net/context"
import (
"github.com/jinzhu/gorm"
"golang.org/x/net/context"
)

type certsService struct {
*Empire
}

func (s *certsService) CertsAttach(ctx context.Context, app *App, cert string) error {
func (s *certsService) CertsAttach(ctx context.Context, db *gorm.DB, app *App, cert string) error {
app.Cert = cert

if err := s.store.AppsUpdate(app); err != nil {
if err := appsUpdate(db, app); err != nil {
return err
}

if err := s.releaser.ReleaseApp(ctx, app); err != nil {
if err := s.releases.ReleaseApp(ctx, db, app); err != nil {
if err == ErrNoReleases {
return nil
}
Expand Down
5 changes: 2 additions & 3 deletions cmd/empire/factories.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/codegangsta/cli"
"github.com/inconshreveable/log15"
"github.com/jinzhu/gorm"
"github.com/remind101/empire"
"github.com/remind101/empire/events/sns"
"github.com/remind101/empire/pkg/dockerutil"
Expand All @@ -24,8 +23,8 @@ import (

// DB ===================================

func newDB(c *cli.Context) (*gorm.DB, error) {
return empire.NewDB(c.String(FlagDB))
func newDB(c *cli.Context) (*empire.DB, error) {
return empire.OpenDB(c.String(FlagDB))
}

// Empire ===============================
Expand Down
10 changes: 6 additions & 4 deletions cmd/empire/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import (
"log"

"github.com/codegangsta/cli"
"github.com/remind101/empire"
)

func runMigrate(c *cli.Context) {
path := c.String(FlagDBPath)
db := c.String(FlagDB)
db, err := newDB(c)
if err != nil {
log.Fatal(err)
}

errors, ok := empire.Migrate(db, path)
path := c.String(FlagDBPath)
errors, ok := db.MigrateUp(path)
if !ok {
log.Fatal(errors)
}
Expand Down
37 changes: 17 additions & 20 deletions configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,11 @@ func (q ConfigsQuery) Scope(db *gorm.DB) *gorm.DB {
return scope.Scope(db)
}

// ConfigsFirst returns the first matching config.
func (s *store) ConfigsFirst(scope Scope) (*Config, error) {
// configsFind returns the first matching config.
func configsFind(db *gorm.DB, scope Scope) (*Config, error) {
var config Config
scope = ComposedScope{Order("created_at desc"), scope}
return &config, s.First(scope, &config)
}

// ConfigsCreate persists the Config.
func (s *store) ConfigsCreate(config *Config) (*Config, error) {
return configsCreate(s.db, config)
return &config, first(db, scope, &config)
}

// ConfigsCreate inserts a Config in the database.
Expand All @@ -123,20 +118,20 @@ type configsService struct {
*Empire
}

func (s *configsService) Set(ctx context.Context, opts SetOpts) (*Config, error) {
func (s *configsService) Set(ctx context.Context, db *gorm.DB, opts SetOpts) (*Config, error) {
app, vars := opts.App, opts.Vars

old, err := s.Config(app)
old, err := s.Config(db, app)
if err != nil {
return nil, err
}

c, err := s.store.ConfigsCreate(NewConfig(old, vars))
c, err := configsCreate(db, NewConfig(old, vars))
if err != nil {
return c, err
}

release, err := s.store.ReleasesFind(ReleasesQuery{App: app})
release, err := releasesFind(db, ReleasesQuery{App: app})
if err != nil {
if err == gorm.RecordNotFound {
err = nil
Expand All @@ -146,7 +141,7 @@ func (s *configsService) Set(ctx context.Context, opts SetOpts) (*Config, error)
}

// Create new release based on new config and old slug
_, err = s.releases.ReleasesCreate(ctx, &Release{
_, err = s.releases.Create(ctx, db, &Release{
App: release.App,
Config: c,
Slug: release.Slug,
Expand All @@ -156,18 +151,20 @@ func (s *configsService) Set(ctx context.Context, opts SetOpts) (*Config, error)
}

// Returns configs for latest release or the latest configs if there are no releases.
func (s *configsService) Config(app *App) (*Config, error) {
r, err := s.store.ReleasesFind(ReleasesQuery{App: app})
func (s *configsService) Config(db *gorm.DB, app *App) (*Config, error) {
r, err := releasesFind(db, ReleasesQuery{App: app})
if err != nil {
if err == gorm.RecordNotFound {
// It's possible to have config without releases, this handles that.
c, err := s.store.ConfigsFirst(ConfigsQuery{App: app})
c, err := configsFind(db, ConfigsQuery{App: app})
if err != nil {
if err == gorm.RecordNotFound {
return s.store.ConfigsCreate(&Config{
App: app,
Vars: make(Vars),
})
// Return an empty config.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually exposed a problem with using created_at to determine what is the current config for an app. There was a case where, when setting config on an app that didn't have any releases, 2 configs would get created at about the same time; 1 empty config and 1 with the update vars. Returning an empty config here fixes that race, but using an auto incremented id to order would probably be better.

return &Config{
AppID: app.ID,
App: app,
Vars: make(Vars),
}, nil
}
return nil, err
}
Expand Down
Loading