Skip to content

Commit f564780

Browse files
committed
feat(pipline): differential deployment
1 parent cbe47eb commit f564780

9 files changed

Lines changed: 394 additions & 172 deletions

File tree

rootfs/api/management/commands/measure_loadbalancers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ def _measure_loadbalancers(self, app_map, timestamp):
3737
"usage": 1,
3838
"kwargs": {
3939
"ip": ip,
40+
"node": item["node"],
4041
"service": item["service"],
41-
"hostname": item["hostname"],
42+
"instance": item["instance"],
4243
},
4344
"timestamp": start
4445
})

rootfs/api/models/app.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,19 @@ def pipeline(self, release, force_deploy=False, rollback_on_failure=True):
256256
self.log(f"{prefix} waiting for pipeline.run: {state} * {count}")
257257
if state != 'down':
258258
raise DryccException(f'pipeline run state error: {state}')
259-
self.log(f"{prefix} starts running pipeline.deploy")
260-
self.deploy(release, None, force_deploy, rollback_on_failure)
259+
procfile_types = release.diff_procfile_types()
260+
if procfile_types is None or len(procfile_types) > 0:
261+
self.log(f"{prefix} starts running pipeline.deploy")
262+
self.deploy(release, procfile_types, force_deploy, rollback_on_failure)
263+
else:
264+
self.log(f"{prefix} no changes, skip executing pipeline.deploy")
261265
release.state = "succeed"
262266
except Exception as e:
263267
release.state = "crashed"
264268
release.failed = True
265269
release.summary = "{} pipeline a release that failed".format(self.owner)
266270
release.exception = "error: {}".format(str(e))
267-
self.log(f"{prefix} pipeline runtime error: {release.exception}")
271+
self.log(f"{prefix} pipeline runtime error: {release.exception}", logging.ERROR)
268272
release.save()
269273
self.log(f"{prefix} run completed...")
270274

@@ -276,7 +280,6 @@ def deploy(self, release, procfile_types=None, force_deploy=False, rollback_on_f
276280
"""
277281
if release.build is None:
278282
raise DryccException('No build associated with this release')
279-
280283
# use create to make sure minimum resources are created
281284
self.create()
282285
# Previous release
@@ -330,7 +333,7 @@ def cleanup_old(self, procfile_types=None):
330333
names.append(self._get_job_id(scale_type, True))
331334
names.append(self._get_job_id(scale_type, False))
332335
labels = {'heritage': 'drycc'}
333-
if procfile_types:
336+
if procfile_types is not None:
334337
labels["type__in"] = procfile_types
335338
deployments = self.scheduler().deployments.get(self.id, labels=labels).json()["items"]
336339
if deployments is not None:

rootfs/api/models/build.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ def create_release(self, user, *args, **kwargs):
9090
new_release = self.app.release_set.latest()
9191
new_release.state = "crashed"
9292
new_release.failed = True
93-
new_release.summary = "{} deployed {} which failed".format(self.owner, str(self.uuid)[:7]) # noqa
93+
new_release.summary = "{} deployed {} which failed".format(
94+
self.owner, str(self.uuid)[:7])
9495
# Get the exception that has occured
9596
new_release.exception = "error: {}".format(str(e))
9697
new_release.save()

rootfs/api/models/gateway.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
DEFAULT_HTTP_PORT = 80
1616
DEFAULT_HTTPS_PORT = 443
1717

18-
HOSTNAME_PROTOCOLS = ("TLS", "HTTP", "HTTPS")
18+
TLS_PROTOCOLS = ("HTTPS", "TLS")
19+
HOSTNAME_PROTOCOLS = TLS_PROTOCOLS + ("HTTP", )
1920

2021

2122
class Gateway(AuditedModel):
@@ -52,23 +53,24 @@ def listeners(self):
5253
for domain in domains:
5354
listener = {
5455
"allowedRoutes": {"namespaces": {"from": "All"}},
55-
"name": self._get_listener_name(port, protocol, domains.index(domain)),
56+
"name": self._get_listener_name(port, protocol, domains.index(domain) + 1),
5657
"port": port,
5758
"hostname": domain.domain,
5859
"protocol": protocol,
5960
}
6061
secret_name = f"{self.app.id}-auto-tls" if auto_tls else (
6162
domain.certificate.name if domain.certificate else None)
62-
if secret_name:
63+
if secret_name and protocol in TLS_PROTOCOLS:
6364
listener["tls"] = {
6465
"certificateRefs": [{"kind": "Secret", "name": secret_name}]}
6566
listeners.append(listener)
66-
listeners.append({
67-
"allowedRoutes": {"namespaces": {"from": "All"}},
68-
"name": self._get_listener_name(port, protocol, 0),
69-
"port": port,
70-
"protocol": protocol,
71-
})
67+
if protocol not in TLS_PROTOCOLS:
68+
listeners.append({
69+
"allowedRoutes": {"namespaces": {"from": "All"}},
70+
"name": self._get_listener_name(port, protocol, 0),
71+
"port": port,
72+
"protocol": protocol,
73+
})
7274
return listeners
7375

7476
@property

rootfs/api/models/release.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.conf import settings
44
from django.db import models
55
from django.contrib.auth import get_user_model
6+
from api.utils import dict_diff
67
from api.tasks import run_pipeline
78
from api.exceptions import DryccException, AlreadyExists
89
from scheduler import KubeHTTPException
@@ -108,6 +109,30 @@ def get_deploy_command(self, container_type):
108109
return self.build.dryccfile.get(
109110
'deploy', {}).get(container_type, {}).get('command', [])
110111

112+
def diff_procfile_types(self):
113+
"""
114+
If returning None, it indicates that all procfile_types have changed.
115+
"""
116+
def _get_full_deploy(release):
117+
deploy = {}
118+
for procfile_type, value in release.build.dryccfile['deploy'].items():
119+
value['image'] = release.get_deploy_image(procfile_type)
120+
value['args'] = release.get_deploy_args(procfile_type)
121+
value['command'] = release.get_deploy_command(procfile_type)
122+
deploy[procfile_type] = value
123+
return deploy
124+
125+
pre_release = self.previous()
126+
if (pre_release and pre_release.build and
127+
pre_release.build.dryccfile and self.build and self.build.dryccfile):
128+
deploy = _get_full_deploy(self)
129+
pre_deploy = _get_full_deploy(pre_release)
130+
procfile_types = set()
131+
for value in dict_diff(deploy, pre_deploy).values():
132+
procfile_types = procfile_types.union(value.keys())
133+
return procfile_types
134+
return None
135+
111136
def log(self, message, level=logging.INFO):
112137
"""Logs a message in the context of this application.
113138
@@ -254,7 +279,7 @@ def cleanup_old(self, procfile_types=None):
254279
)
255280
# base labels
256281
labels = {'heritage': 'drycc'}
257-
if procfile_types:
282+
if procfile_types is not None:
258283
labels['type__in'] = procfile_types
259284
# Cleanup controllers
260285
replica_sets_removal = []
@@ -286,7 +311,7 @@ def cleanup_old(self, procfile_types=None):
286311

287312
def _cleanup_stray_pods(self, namespace, procfile_types, latest_version):
288313
labels = {'heritage': 'drycc'}
289-
if procfile_types:
314+
if procfile_types is not None:
290315
labels['type__in'] = procfile_types
291316
pods = self.scheduler().pod.get(namespace, labels=labels).json()['items']
292317
if not pods:
@@ -340,7 +365,7 @@ def _cleanup_deployment_secrets_and_configs(self, namespace, procfile_types=None
340365
# http://kubernetes.io/docs/user-guide/labels/#set-based-requirement
341366
'version__notin': versions
342367
}
343-
if procfile_types:
368+
if procfile_types is not None:
344369
labels['type__in'] = procfile_types
345370
self.log('Cleaning up orphaned env var secrets for application {}'.format(namespace), level=logging.DEBUG) # noqa
346371
secrets = self.scheduler().secret.get(namespace, labels=labels).json()['items']
@@ -361,7 +386,7 @@ def _delete_release_in_scheduler(self, namespace, procfile_types, version):
361386
'app': namespace,
362387
'version': version
363388
}
364-
if procfile_types:
389+
if procfile_types is not None:
365390
labels['type__in'] = procfile_types
366391
# see if the app config has deploy timeout preference, otherwise use global
367392
timeout = self.config.values.get('DRYCC_DEPLOY_TIMEOUT', settings.DRYCC_DEPLOY_TIMEOUT)

rootfs/api/models/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def as_dict(self):
5959
}
6060

6161
def port_name(self, port, protocol):
62-
return "-".join([self.app.id, self.procfile_type, protocol, str(port)]).lower()
62+
return "-".join([protocol, str(port)]).lower()
6363

6464
def get_port(self, port, protocol):
6565
for item in self.ports:

0 commit comments

Comments
 (0)