Skip to content

Commit be3d205

Browse files
committed
chore(controller): improve the details of certificate
1 parent ff5ee53 commit be3d205

11 files changed

Lines changed: 76 additions & 50 deletions

File tree

charts/controller/templates/controller-certificate.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ kind: Certificate
44
metadata:
55
name: drycc-controller
66
spec:
7-
secretName: drycc-controller-auto-tls
7+
secretName: drycc-controller-certificate-auto
88
issuerRef:
99
name: drycc-cluster-issuer
1010
kind: ClusterIssuer

charts/controller/templates/controller-ingress.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ spec:
2727
servicePort: 80
2828
{{ if .Values.global.cert_manager_enabled }}
2929
tls:
30-
- secretName: drycc-controller-auto-tls
30+
- secretName: drycc-controller-certificate-auto
3131
hosts:
3232
- drycc.{{ .Values.global.platform_domain }}
3333
{{- end }}

rootfs/api/models/app.py

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import backoff
22
import base64
33
import math
4-
from collections import OrderedDict
4+
from collections import OrderedDict, defaultdict
55
from datetime import datetime
66
from docker import auth as docker_auth
77
import functools
@@ -162,7 +162,7 @@ def _get_entrypoint(self, container_type):
162162

163163
return entrypoint
164164

165-
def _refresh_tls(self, certs_auto_enabled, hosts):
165+
def _refresh_certificate(self, certs_auto_enabled, hosts):
166166
namespace = name = self.id
167167
try:
168168
data = self._scheduler.certificate.get(namespace, name).json()
@@ -208,35 +208,27 @@ def _refresh_ingress(self, hosts, tls_map, ssl_redirect):
208208
except KubeException as e:
209209
raise ServiceUnavailable('Could not create Ingress in Kubernetes') from e
210210

211-
def refresh_ingress_and_tls(self):
212-
if not getattr(self, 'refresh_ingress_and_tls_enabled', True):
211+
def refresh(self):
212+
if not getattr(self, 'refresh_enabled', True):
213213
return
214214
app_settings = self.appsettings_set.latest()
215215
if not app_settings.routable:
216216
return
217-
ingress = self.id
218-
hosts, tls_map = [], {}
219-
220217
tls = self.tls_set.latest()
221218
ssl_redirect = "true" if bool(tls.https_enforced) else "false"
222219
certs_auto_enabled = bool(tls.certs_auto_enabled)
223-
220+
hosts, tls_map = [], defaultdict(list)
224221
for domain in Domain.objects.filter(app=self):
225-
if str(domain.domain) == self.id:
226-
host = "%s.%s" % (ingress, settings.PLATFORM_DOMAIN)
227-
else:
228-
host = str(domain.domain)
222+
host = str(domain.domain)
229223
hosts.append(host)
230-
if certs_auto_enabled or domain.certificate:
231-
if certs_auto_enabled:
232-
secret_name = '%s-auto-tls' % self.id
233-
elif domain.certificate:
234-
secret_name = '%s-cert' % domain.certificate.name
235-
if secret_name not in tls_map:
236-
tls_map[secret_name] = []
224+
if domain.certificate:
225+
secret_name = '%s-certificate' % domain.certificate.name
226+
tls_map[secret_name].append(host)
227+
if certs_auto_enabled and not domain.domain.startswith("*."):
228+
secret_name = '%s-certificate-auto' % self.id
237229
tls_map[secret_name].append(host)
238-
self._refresh_ingress(hosts, tls_map, ssl_redirect)
239-
self._refresh_tls(certs_auto_enabled, hosts)
230+
self._refresh_ingress(hosts, dict(tls_map), ssl_redirect)
231+
self._refresh_certificate(certs_auto_enabled, hosts)
240232

241233
def log(self, message, level=logging.INFO):
242234
"""Logs a message in the context of this application.
@@ -311,7 +303,7 @@ def create(self, *args, **kwargs): # noqa
311303

312304
raise ServiceUnavailable('Kubernetes resources could not be created') from e
313305
try:
314-
setattr(self, 'refresh_ingress_and_tls_enabled', False) # do not refresh
306+
setattr(self, 'refresh_enabled', False) # do not refresh
315307
try:
316308
self.appsettings_set.latest()
317309
except AppSettings.DoesNotExist:
@@ -322,12 +314,13 @@ def create(self, *args, **kwargs): # noqa
322314
TLS.objects.create(owner=self.owner, app=self)
323315
# Attach the platform specific application sub domain to the k8s service
324316
# Only attach it on first release in case a customer has remove the app domain
325-
if rel.version == 1 and not Domain.objects.filter(domain=self.id).exists():
326-
Domain.objects.create(owner=self.owner, app=self, domain=self.id)
317+
domain = "%s.%s" % (self.id, settings.PLATFORM_DOMAIN)
318+
if rel.version == 1 and not Domain.objects.filter(domain=domain).exists():
319+
Domain.objects.create(owner=self.owner, app=self, domain=domain)
327320
# The default routable is true, so refresh ingress and tls
328321
finally:
329-
setattr(self, 'refresh_ingress_and_tls_enabled', True)
330-
self.refresh_ingress_and_tls() # refresh
322+
setattr(self, 'refresh_enabled', True)
323+
self.refresh() # refresh
331324

332325
def delete(self, *args, **kwargs):
333326
"""Delete this application including all containers"""
@@ -968,7 +961,7 @@ def routable(self, routable):
968961
Turn on/off if an application is publically routable
969962
"""
970963
if routable:
971-
self.refresh_ingress_and_tls()
964+
self.refresh()
972965
else:
973966
try:
974967
namespace = ingress = self.id

rootfs/api/models/appsettings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,5 @@ def save(self, *args, **kwargs):
182182
try:
183183
return super(AppSettings, self).save(**kwargs)
184184
finally:
185-
self.app.refresh_ingress_and_tls()
185+
self.app.refresh()
186186
self.app.log('summary of app setting changes: {}'.format(summary), logging.DEBUG)

rootfs/api/models/certificate.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def attach_in_kubernetes(self, domain):
181181
"""Creates the certificate as a kubernetes secret"""
182182
# only create if it exists - We raise an exception when a secret doesn't exist
183183
try:
184-
name = '%s-cert' % self.name
184+
name = '%s-certificate' % self.name
185185
namespace = domain.app.id
186186
data = {
187187
'tls.crt': self.certificate,
@@ -207,7 +207,7 @@ def detach(self, *args, **kwargs):
207207
domain.certificate = None
208208
domain.save()
209209

210-
name = '%s-cert' % self.name
210+
name = '%s-certificate' % self.name
211211
namespace = domain.app.id
212212

213213
# only delete if it exists and if no other domains depend on secret
@@ -217,4 +217,4 @@ def detach(self, *args, **kwargs):
217217
self._scheduler.secret.get(namespace, name)
218218
self._scheduler.secret.delete(namespace, name)
219219
except KubeException as e:
220-
raise ServiceUnavailable("Could not delete certificate secret {} for application {}".format(name, namespace)) from e # noqa
220+
raise ServiceUnavailable("Could not delete certificate secret {} for application {}".format(name, namespace)) from e # noqa

rootfs/api/models/domain.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def save(self, *args, **kwargs):
3030
# Save to DB
3131
return super(Domain, self).save(*args, **kwargs)
3232
finally:
33-
self.app.refresh_ingress_and_tls()
33+
self.app.refresh()
3434

3535
@transaction.atomic
3636
def delete(self, *args, **kwargs):
@@ -41,7 +41,7 @@ def delete(self, *args, **kwargs):
4141
# Delete from DB
4242
return super(Domain, self).delete(*args, **kwargs)
4343
finally:
44-
self.app.refresh_ingress_and_tls()
44+
self.app.refresh()
4545

4646
def __str__(self):
4747
return self.domain

rootfs/api/models/tls.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def save(self, *args, **kwargs):
4848
# Save to DB
4949
return super(TLS, self).save(*args, **kwargs)
5050
finally:
51-
self.app.refresh_ingress_and_tls()
51+
self.app.refresh()
5252

5353
def sync(self):
54-
self.app.refresh_ingress_and_tls()
54+
self.app.refresh()

rootfs/api/settings/production.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,9 @@
336336
KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS = int(os.environ.get('KUBERNETES_POD_TERMINATION_GRACE_PERIOD_SECONDS', 30)) # noqa
337337

338338
# Minimum allocation cpu, units are represented in the millicpu of CPUs
339-
KUBERNETES_CPU_MIN_ALLOCATION = int(os.environ.get('KUBERNETES_RAM_MIN_ALLOCATION', '100'))
339+
KUBERNETES_CPU_MIN_ALLOCATION = int(os.environ.get('KUBERNETES_RAM_MIN_ALLOCATION', '10'))
340340
# Minimum allocation memory, units are represented in Megabytes(M)
341-
KUBERNETES_RAM_MIN_ALLOCATION = int(os.environ.get('KUBERNETES_RAM_MIN_ALLOCATION', '128'))
341+
KUBERNETES_RAM_MIN_ALLOCATION = int(os.environ.get('KUBERNETES_RAM_MIN_ALLOCATION', '64'))
342342
# Maximum allocation cpu, units are represented in the millicpu of CPUs
343343
KUBERNETES_CPU_MAX_ALLOCATION = int(os.environ.get('KUBERNETES_RAM_MIN_ALLOCATION', '32000'))
344344
# Maximum allocation memory, units are represented in Megabytes(M)

rootfs/api/tests/test_domain.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from django.contrib.auth.models import User
99
from django.core.cache import cache
10+
from django.conf import settings
1011
from rest_framework.authtoken.models import Token
1112

1213
from api.models import Domain
@@ -66,7 +67,9 @@ def test_strip_dot(self):
6667
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
6768
response = self.client.get(url)
6869
expected = [data['domain'] for data in response.data['results']]
69-
self.assertEqual(sorted([self.app_id, domain]), expected, msg)
70+
self.assertEqual(
71+
sorted(["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN), domain]),
72+
expected, msg)
7073

7174
def test_manage_idn_domain(self):
7275
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
@@ -102,7 +105,9 @@ def test_manage_idn_domain(self):
102105
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
103106
response = self.client.get(url)
104107
expected = [data['domain'] for data in response.data['results']]
105-
self.assertEqual(sorted([self.app_id, ace_domain]), sorted(expected), msg)
108+
self.assertEqual(
109+
sorted(["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN), ace_domain]),
110+
sorted(expected), msg)
106111

107112
# Verify creation failure for same domain with different encoding
108113
if ace_domain != domain:
@@ -127,7 +132,9 @@ def test_manage_idn_domain(self):
127132

128133
# verify only app domain is left
129134
expected = [data['domain'] for data in response.data['results']]
130-
self.assertEqual([self.app_id], expected, msg)
135+
self.assertEqual(
136+
["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN)],
137+
expected, msg)
131138

132139
# Use different encoding for creating and deleting (ACE)
133140
if ace_domain != domain:
@@ -139,7 +146,9 @@ def test_manage_idn_domain(self):
139146
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
140147
response = self.client.get(url)
141148
expected = [data['domain'] for data in response.data['results']]
142-
self.assertEqual(sorted([self.app_id, ace_domain]), sorted(expected), msg)
149+
self.assertEqual(
150+
sorted(["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN), ace_domain]),
151+
sorted(expected), msg)
143152

144153
# Delete
145154
url = '/v2/apps/{app_id}/domains/{hostname}'.format(hostname=ace_domain,
@@ -154,7 +163,9 @@ def test_manage_idn_domain(self):
154163

155164
# verify only app domain is left
156165
expected = [data['domain'] for data in response.data['results']]
157-
self.assertEqual([self.app_id], expected, msg)
166+
self.assertEqual(
167+
["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN)],
168+
expected, msg)
158169

159170
# Use different encoding for creating and deleting (Unicode)
160171
if unicode_domain != domain:
@@ -166,7 +177,9 @@ def test_manage_idn_domain(self):
166177
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
167178
response = self.client.get(url)
168179
expected = [data['domain'] for data in response.data['results']]
169-
self.assertEqual(sorted([self.app_id, ace_domain]), sorted(expected), msg)
180+
self.assertEqual(
181+
sorted(["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN), ace_domain]),
182+
sorted(expected), msg)
170183

171184
# Delete
172185
url = '/v2/apps/{app_id}/domains/{hostname}'.format(hostname=unicode_domain,
@@ -181,7 +194,9 @@ def test_manage_idn_domain(self):
181194

182195
# verify only app domain is left
183196
expected = [data['domain'] for data in response.data['results']]
184-
self.assertEqual([self.app_id], expected, msg)
197+
self.assertEqual(
198+
["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN)],
199+
expected, msg)
185200

186201
def test_manage_domain(self):
187202
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
@@ -211,7 +226,9 @@ def test_manage_domain(self):
211226
url = '/v2/apps/{app_id}/domains'.format(app_id=self.app_id)
212227
response = self.client.get(url)
213228
expected = [data['domain'] for data in response.data['results']]
214-
self.assertEqual(sorted([self.app_id, domain]), sorted(expected), msg)
229+
self.assertEqual(
230+
sorted(["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN), domain]),
231+
sorted(expected), msg)
215232

216233
# Delete
217234
url = '/v2/apps/{app_id}/domains/{hostname}'.format(hostname=domain,
@@ -226,7 +243,9 @@ def test_manage_domain(self):
226243

227244
# verify only app domain is left
228245
expected = [data['domain'] for data in response.data['results']]
229-
self.assertEqual([self.app_id], expected, msg)
246+
self.assertEqual(
247+
["%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN)],
248+
expected, msg)
230249

231250
def test_delete_domain_does_not_exist(self):
232251
"""Remove a domain that does not exist"""
@@ -253,6 +272,13 @@ def test_delete_domain_does_not_remove_latest(self):
253272
with self.assertRaises(Domain.DoesNotExist):
254273
Domain.objects.get(domain=test_domains[0])
255274

275+
def test_delete_domain_does_not_remove_default(self):
276+
domain = "%s.%s" % (self.app_id, settings.PLATFORM_DOMAIN)
277+
url = '/v2/apps/{app_id}/domains/{domain}'.format(domain=domain,
278+
app_id=self.app_id)
279+
response = self.client.delete(url)
280+
self.assertEqual(response.status_code, 403, response.data)
281+
256282
def test_delete_domain_does_not_remove_others(self):
257283
"""https://github.com/drycc/drycc/issues/3475"""
258284
self.test_delete_domain_does_not_remove_latest()

rootfs/api/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,10 +375,17 @@ def get_object(self, **kwargs):
375375
ace_domain = "*." + idna.encode(domain[2:]).decode("utf-8", "strict")
376376
else:
377377
ace_domain = idna.encode(domain).decode("utf-8", "strict")
378-
except:
378+
except: # noqa
379379
ace_domain = domain
380380
return get_object_or_404(qs, domain=ace_domain)
381381

382+
def destroy(self, request, **kwargs):
383+
domain = self.get_object()
384+
if "%s.%s" % (domain.app.id, settings.PLATFORM_DOMAIN) == domain.domain:
385+
return Response(status=status.HTTP_403_FORBIDDEN)
386+
domain.delete()
387+
return Response(status=status.HTTP_204_NO_CONTENT)
388+
382389

383390
class ServiceViewSet(AppResourceViewSet):
384391
"""A viewset for interacting with Service objects."""

0 commit comments

Comments
 (0)