Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.

Commit 169f40c

Browse files
committed
Merge pull request #64 from getsentry/taskconfig
Split TaskConfig off of App
2 parents 8da10b9 + ffab347 commit 169f40c

23 files changed

+254
-63
lines changed

Procfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
web: bin/upgrade && PYTHONUNBUFFERED=1 bin/web --no-debug --addr=:${PORT}
1+
web: bin/upgrade && PYTHONUNBUFFERED=1 bin/web --addr=:${PORT} --debug
22
worker: bin/upgrade && PYTHONUNBUFFERED=1 bin/worker --no-debug -n 4 -l ${LOG_LEVEL}
3+
static: node_modules/.bin/webpack -d --watch

freight/api/app_details.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from freight.checks.utils import parse_checks_config
1010
from freight.config import db, queue
1111
from freight.environments.utils import parse_environments_config
12-
from freight.models import App, Repository
12+
from freight.models import App, Repository, TaskConfig, TaskConfigType
1313
from freight.notifiers.utils import parse_notifiers_config
1414
from freight.providers.utils import parse_provider_config
1515

@@ -20,12 +20,19 @@ def get(self, app):
2020
if app is None:
2121
return self.error('Invalid app', name='invalid_resource', status_code=404)
2222

23+
deploy_config = TaskConfig.query.filter(
24+
TaskConfig.app_id == app.id,
25+
TaskConfig.type == TaskConfigType.deploy,
26+
).first()
27+
if deploy_config is None:
28+
return self.error('Missing deploy config', name='missing_conf', status_code=404)
29+
2330
context = serialize(app)
2431
context.update({
25-
'provider': app.provider,
26-
'provider_config': app.provider_config,
27-
'notifiers': app.notifiers,
28-
'checks': app.checks,
32+
'provider': deploy_config.provider,
33+
'provider_config': deploy_config.provider_config,
34+
'notifiers': deploy_config.notifiers,
35+
'checks': deploy_config.checks,
2936
})
3037

3138
return self.respond(context)
@@ -49,27 +56,38 @@ def put(self, app):
4956
if app is None:
5057
return self.error('Invalid app', name='invalid_resource', status_code=404)
5158

59+
# For backwards compatibility, we assume that we need a deploy TaskConfig
60+
# on the app at all times.
61+
deploy_config = TaskConfig.query.filter(
62+
TaskConfig.app_id == app.id,
63+
TaskConfig.type == TaskConfigType.deploy,
64+
).first()
65+
if deploy_config is None:
66+
deploy_config = TaskConfig(app_id=app.id, type=TaskConfigType.deploy)
67+
db.session.add(deploy_config)
68+
db.session.flush()
69+
5270
if args.provider or args.provider_config:
5371
if args.provider is not None:
5472
provider = args.provider
5573
else:
56-
provider = app.provider
74+
provider = deploy_config.provider
5775

5876
if args.provider_config is not None:
5977
provider_config = args.provider_config
6078
else:
61-
provider_config = app.provider_config
79+
provider_config = deploy_config.provider_config
6280

63-
app.provider = provider
64-
app.data['provider_config'] = parse_provider_config(
81+
deploy_config.provider = provider
82+
deploy_config.data['provider_config'] = parse_provider_config(
6583
provider, provider_config
6684
)
6785

6886
if args.notifiers is not None:
69-
app.data['notifiers'] = parse_notifiers_config(args.notifiers)
87+
deploy_config.data['notifiers'] = parse_notifiers_config(args.notifiers)
7088

7189
if args.checks is not None:
72-
app.data['checks'] = parse_checks_config(args.checks)
90+
deploy_config.data['checks'] = parse_checks_config(args.checks)
7391

7492
if args.environments is not None:
7593
app.data['environments'] = parse_environments_config(args.environments)
@@ -89,14 +107,15 @@ def put(self, app):
89107
app.repository_id = repo.id
90108

91109
db.session.add(app)
110+
db.session.add(deploy_config)
92111
db.session.commit()
93112

94113
context = serialize(app)
95114
context.update({
96-
'provider': app.provider,
97-
'provider_config': app.provider_config,
98-
'notifiers': app.notifiers,
99-
'checks': app.checks,
115+
'provider': deploy_config.provider,
116+
'provider_config': deploy_config.provider_config,
117+
'notifiers': deploy_config.notifiers,
118+
'checks': deploy_config.checks,
100119
})
101120

102121
return self.respond(context)

freight/api/app_index.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from freight.checks.utils import parse_checks_config
1010
from freight.config import db
1111
from freight.environments.utils import parse_environments_config
12-
from freight.models import App, Repository
12+
from freight.models import App, Repository, TaskConfig, TaskConfigType
1313
from freight.notifiers.utils import parse_notifiers_config
1414
from freight.providers.utils import parse_provider_config
1515

@@ -68,15 +68,26 @@ def post(self):
6868
app = App(
6969
name=args.name,
7070
repository_id=repo.id,
71+
data={
72+
'environments': environments_config,
73+
},
74+
)
75+
db.session.add(app)
76+
db.session.flush()
77+
78+
# For backwards compatibility, we assume that we need a deploy TaskConfig
79+
# on the app at all times.
80+
deploy_config = TaskConfig(
81+
app_id=app.id,
82+
type=TaskConfigType.deploy,
7183
provider=args.provider,
7284
data={
7385
'provider_config': provider_config,
7486
'notifiers': notifiers_config,
7587
'checks': checks_config,
76-
'environments': environments_config,
7788
},
7889
)
79-
db.session.add(app)
90+
db.session.add(deploy_config)
8091
db.session.commit()
8192

8293
return self.respond(serialize(app), status_code=201)

freight/api/deploy_index.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from freight.config import db, redis
1111
from freight.exceptions import CheckError, CheckPending
1212
from freight.models import (
13-
App, Repository, Task, Deploy, DeploySequence, TaskStatus, User
13+
App, Repository, Task, Deploy, DeploySequence, TaskStatus, User,
14+
TaskConfig, TaskConfigType,
1415
)
1516
from freight.notifiers import NotifierEvent
1617
from freight.notifiers.utils import send_task_notifications
@@ -114,6 +115,13 @@ def post(self):
114115
if not app:
115116
return self.error('Invalid app', name='invalid_resource', status_code=404)
116117

118+
deploy_config = TaskConfig.query.filter(
119+
TaskConfig.app_id == app.id,
120+
TaskConfig.type == TaskConfigType.deploy,
121+
).first()
122+
if not deploy_config:
123+
return self.error('Missing deploy config', name='missing_conf', status_code=404)
124+
117125
params = None
118126

119127
repo = Repository.query.get(app.repository_id)
@@ -149,7 +157,7 @@ def post(self):
149157
params = args.params
150158

151159
if not args.force:
152-
for check_config in app.checks:
160+
for check_config in deploy_config.checks:
153161
check = checks.get(check_config['type'])
154162
try:
155163
check.check(app, sha, check_config['config'])
@@ -170,12 +178,12 @@ def post(self):
170178
params=params,
171179
status=TaskStatus.pending,
172180
user_id=user.id,
173-
provider=app.provider,
181+
provider=deploy_config.provider,
174182
data={
175183
'force': args.force,
176-
'provider_config': app.provider_config,
177-
'notifiers': app.notifiers,
178-
'checks': app.checks,
184+
'provider_config': deploy_config.provider_config,
185+
'notifiers': deploy_config.notifiers,
186+
'checks': deploy_config.checks,
179187
},
180188
)
181189
db.session.add(task)

freight/models/app.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,18 @@ class App(db.Model):
6161
data = Column(JSONEncodedDict)
6262
date_created = Column(DateTime, default=datetime.utcnow, nullable=False)
6363

64-
@property
65-
def checks(self):
66-
return self.data.get('checks', [])
67-
68-
@property
69-
def notifiers(self):
70-
return self.data.get('notifiers', [])
71-
72-
@property
73-
def provider_config(self):
74-
return self.data.get('provider_config', {})
75-
7664
@property
7765
def environments(self):
7866
return self.data.get('environments', {})
7967

68+
@property
69+
def deploy_config(self):
70+
from freight.models import TaskConfig, TaskConfigType
71+
return TaskConfig.query.filter(
72+
TaskConfig.app_id == self.id,
73+
TaskConfig.type == TaskConfigType.deploy,
74+
).first()
75+
8076
def get_default_ref(self, env):
8177
data = self.environments.get(env)
8278
if not data:

freight/models/taskconfig.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from __future__ import absolute_import
2+
3+
from sqlalchemy import Column, ForeignKey, Integer, String
4+
from sqlalchemy.schema import Index, UniqueConstraint
5+
6+
from freight.config import db
7+
from freight.db.types.json import JSONEncodedDict
8+
9+
10+
class TaskConfigType(object):
11+
deploy = 0
12+
13+
@classmethod
14+
def get_label(cls, status):
15+
return TYPE_LABELS[status]
16+
17+
@classmethod
18+
def label_to_id(cls, label):
19+
return TYPE_LABELS_REV[label]
20+
21+
22+
TYPE_LABELS = {
23+
TaskConfigType.deploy: 'deploy',
24+
}
25+
TYPE_LABELS_REV = {
26+
v: k for k, v in TYPE_LABELS.items()
27+
}
28+
29+
30+
class TaskConfig(db.Model):
31+
__tablename__ = 'taskconfig'
32+
__table_args__ = (
33+
Index('idx_taskconfig_app_id', 'app_id'),
34+
Index('idx_taskconfig_type', 'type'),
35+
UniqueConstraint('app_id', 'type', name='unq_app_id_type'),
36+
)
37+
38+
id = Column(Integer, primary_key=True)
39+
app_id = Column(Integer, ForeignKey('app.id', ondelete="CASCADE"),
40+
nullable=False)
41+
provider = Column(String(64), nullable=False)
42+
type = Column(Integer)
43+
data = Column(JSONEncodedDict)
44+
45+
@property
46+
def checks(self):
47+
return self.data.get('checks', [])
48+
49+
@property
50+
def notifiers(self):
51+
return self.data.get('notifiers', [])
52+
53+
@property
54+
def provider_config(self):
55+
return self.data.get('provider_config', {})
56+
57+
@property
58+
def environments(self):
59+
return self.data.get('environments', {})
60+
61+
@property
62+
def type_label(self):
63+
return TYPE_LABELS[self.type]

freight/testutils/fixtures.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66

77
from freight.config import db
88
from freight.constants import PROJECT_ROOT
9-
from freight.models import App, Repository, Task, DeploySequence, Deploy, TaskStatus, User
9+
from freight.models import (
10+
App, Repository, Task, DeploySequence, Deploy, TaskStatus, User,
11+
TaskConfig, TaskConfigType,
12+
)
1013

1114

1215
class Fixtures(object):
13-
def create_app(self, repository, **kwargs):
14-
if not kwargs.get('name'):
15-
kwargs['name'] = uuid4().hex
16-
16+
def create_taskconfig(self, app, **kwargs):
17+
kwargs.setdefault('type', TaskConfigType.deploy)
1718
kwargs.setdefault('provider', 'shell')
1819
kwargs.setdefault('data', {
1920
'provider_config': {
@@ -25,6 +26,19 @@ def create_app(self, repository, **kwargs):
2526
'config': {'webhook_url': 'https://example.com'},
2627
},
2728
],
29+
})
30+
31+
task_config = TaskConfig(app_id=app.id, **kwargs)
32+
db.session.add(task_config)
33+
db.session.commit()
34+
35+
return task_config
36+
37+
def create_app(self, repository, **kwargs):
38+
if not kwargs.get('name'):
39+
kwargs['name'] = uuid4().hex
40+
41+
kwargs.setdefault('data', {
2842
'environments': {
2943
'production': {
3044
'default_ref': 'master',
@@ -46,7 +60,7 @@ def create_task(self, app, user, **kwargs):
4660
kwargs.setdefault('ref', 'master')
4761
kwargs.setdefault('sha', 'HEAD')
4862
kwargs.setdefault('status', TaskStatus.in_progress)
49-
kwargs.setdefault('data', {'provider_config': app.provider_config})
63+
kwargs.setdefault('data', {'provider_config': app.deploy_config.provider_config})
5064
kwargs.setdefault('params', {'task': 'deploy'})
5165

5266
task = Task(
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""
2+
Added TaskConfig
3+
4+
Revision ID: 205fd513c96
5+
Revises: 106230ad9e69
6+
Create Date: 2016-03-07 13:27:04.392376
7+
"""
8+
9+
# revision identifiers, used by Alembic.
10+
revision = '205fd513c96'
11+
down_revision = '106230ad9e69'
12+
13+
from alembic import op
14+
from sqlalchemy.sql import table
15+
import sqlalchemy as sa
16+
import freight
17+
18+
19+
def upgrade():
20+
taskconfig_table = op.create_table(
21+
'taskconfig',
22+
sa.Column('id', sa.Integer(), nullable=False),
23+
sa.Column('app_id', sa.Integer(), nullable=False),
24+
sa.Column('provider', sa.String(length=64), nullable=False),
25+
sa.Column('type', sa.Integer(), nullable=True),
26+
sa.Column('data', freight.db.types.json.JSONEncodedDict(), nullable=True),
27+
sa.ForeignKeyConstraint(['app_id'], ['app.id'], ondelete='CASCADE'),
28+
sa.PrimaryKeyConstraint('id'),
29+
sa.UniqueConstraint('app_id', 'type', name='unq_app_id_type')
30+
)
31+
op.create_index('idx_taskconfig_app_id', 'taskconfig', ['app_id'], unique=False)
32+
op.create_index('idx_taskconfig_type', 'taskconfig', ['type'], unique=False)
33+
34+
connection = op.get_bind()
35+
36+
app_table = table(
37+
'app',
38+
sa.Column('id', sa.Integer(), nullable=False),
39+
sa.Column('provider', sa.String(length=54), nullable=False),
40+
sa.Column('data', freight.db.types.json.JSONEncodedDict(), nullable=True)
41+
)
42+
43+
# Copy over the existing configs out of the App table and into TaskConfigs
44+
for app in connection.execute(app_table.select()):
45+
print("Migrating App id=%s" % app.id)
46+
47+
op.bulk_insert(
48+
taskconfig_table,
49+
[
50+
{'app_id': app.id, 'type': 0, 'provider': app.provider, 'data': app.data},
51+
],
52+
)
53+
54+
55+
def downgrade():
56+
op.drop_index('idx_taskconfig_type', table_name='taskconfig')
57+
op.drop_index('idx_taskconfig_app_id', table_name='taskconfig')
58+
op.drop_table('taskconfig')

0 commit comments

Comments
 (0)