-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathservice.py
More file actions
116 lines (95 loc) · 4.69 KB
/
service.py
File metadata and controls
116 lines (95 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import logging
from django.db import models
from django.conf import settings
from api.models import AuditedModel, UuidAuditedModel, AlreadyExists, DeisException, ServiceUnavailable
from scheduler import KubeException
logger = logging.getLogger(__name__)
class Service(AuditedModel):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
app = models.ForeignKey('App', on_delete=models.CASCADE)
procfile_type = models.TextField(blank=False, null=False, unique=False)
path_pattern = models.TextField(blank=False, null=False, unique=False)
class Meta:
get_latest_by = 'created'
unique_together = (('app', 'procfile_type'))
ordering = ['-created']
def __str__(self):
return "{}-{}".format(self.app.id, str(self.procfile_type))
def as_dict(self):
return {
"procfile_type": self.procfile_type,
"path_pattern": self.path_pattern
}
def create(self, *args, **kwargs): # noqa
# create required minimum service in k8s for the application
namespace = self.app.id
svc_name = "{}-{}".format(namespace, self.procfile_type)
self.log('creating Service: {}'.format(svc_name), level=logging.DEBUG)
try:
try:
self._scheduler.svc.get(namespace, svc_name)
except KubeException:
self._scheduler.svc.create(namespace, svc_name)
except KubeException as e:
raise ServiceUnavailable('Kubernetes service could not be created') from e
# config service
annotations = self._gather_settings()
routable = annotations.pop('routable')
self._update_service(namespace, self.procfile_type, routable, annotations)
def save(self, *args, **kwargs):
service = super(Service, self).save(*args, **kwargs)
self.create()
return service
def delete(self, *args, **kwargs):
namespace = self.app.id
svc_name = "{}-{}".format(namespace, self.procfile_type)
self.log('deleting Service: {}'.format(svc_name), level=logging.DEBUG)
try:
self._scheduler.svc.delete(namespace, svc_name)
except KubeException:
# swallow exception
# raise ServiceUnavailable('Kubernetes service could not be deleted') from e
self.log('Kubernetes service cannot be deleted: {}'.format(svc_name), level=logging.ERROR)
# Delete from DB
return super(Service, self).delete(*args, **kwargs)
def log(self, message, level=logging.INFO):
"""Logs a message in the context of this service.
This prefixes log messages with an application "tag" that the customized deis-logspout will
be on the lookout for. When it's seen, the message-- usually an application event of some
sort like releasing or scaling, will be considered as "belonging" to the application
instead of the controller and will be handled accordingly.
"""
logger.log(level, "[{}]: {}".format(self.id, message))
def _gather_settings(self):
app_settings = self.app.appsettings_set.latest()
return {
'domains': "{}-{}".format(self.app.id, self.procfile_type),
'maintenance': app_settings.maintenance,
'routable': app_settings.routable,
'proxyDomain': self.app.id,
'proxyLocations': self.path_pattern
}
def _update_service(self, namespace, app_type, routable, annotations): # noqa
"""Update application service with all the various required information"""
svc_name = "{}-{}".format(namespace, app_type)
service = self._fetch_service_config(namespace, svc_name)
old_service = service.copy() # in case anything fails for rollback
try:
# Update service information
for key, value in annotations.items():
if value is not None:
service['metadata']['annotations']['router.deis.io/%s' % key] = str(value)
else:
service['metadata']['annotations'].pop('router.deis.io/%s' % key, None)
if routable:
service['metadata']['labels']['router.deis.io/routable'] = 'true'
else:
# delete the annotation
service['metadata']['labels'].pop('router.deis.io/routable', None)
# Set app type selector
service['spec']['selector']['type'] = app_type
self._scheduler.svc.update(namespace, svc_name, data=service)
except Exception as e:
# Fix service to old port and app type
self._scheduler.svc.update(namespace, svc_name, data=old_service)
raise ServiceUnavailable(str(e)) from e