Skip to content

Commit dc6357a

Browse files
committed
chore(volume): add reserved name patterns
1 parent 82faa52 commit dc6357a

9 files changed

Lines changed: 60 additions & 50 deletions

File tree

charts/controller/templates/_helpers.tpl

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -320,28 +320,20 @@ kubelet_volume_stats_inodes_free: [namespace, persistentvolumeclaim, job]
320320
kubelet_volume_stats_inodes_used: [namespace, persistentvolumeclaim, job]
321321
{{- end }}
322322

323-
{{/* Generate controller config default reserved names */}}
324-
{{ define "controller.config.defaultReservedNames" }}
325-
backup
326-
catalog
327-
cert-manager
328-
default
329-
drycc
330-
drycc-manager
331-
drycc-helmbroker
332-
drycc-builder
333-
drycc-grafana
334-
drycc-passport
335-
istio-gateway
336-
istio-system
337-
kube-node-lease
338-
kube-public
339-
kube-system
340-
longhorn-system
341-
metallb
342-
mount-s3
343-
topolvm
344-
rook-ceph
323+
{{/* Generate controller config default reserved name patterns */}}
324+
{{ define "controller.config.defaultReservedNamePatterns" }}
325+
^backup$
326+
^catalog$
327+
^cert-manager$
328+
^default$
329+
^drycc(?:-[\w-]+)?$
330+
^istio(?:-[\w-]+)?$
331+
^kube(?:-[\w-]+)?$
332+
^longhorn-system$
333+
^metallb$
334+
^mount-s3$
335+
^topolvm$
336+
^rook-ceph$
345337
{{- end }}
346338

347339
{{/* Generate controller config default secrets template */}}

charts/controller/templates/controller-configmap.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ data:
2323
{{- else}}
2424
{{- include "controller.config.defaultLimitPlans" . | fromYamlArray | toPrettyJson | nindent 4 }}
2525
{{- end }}
26-
reserved-names.txt: |-
27-
{{- if .Values.config.reservedNames }}
28-
{{- (tpl .Values.config.reservedNames $) | nindent 4 }}
29-
{{- else}}
30-
{{- include "controller.config.defaultReservedNames" . | nindent 4 }}
31-
{{- end }}
3226
secret-template.json: |
3327
{{- if .Values.config.secretTemplate }}
3428
{{- (tpl .Values.config.secretTemplate $) | nindent 4 }}
@@ -41,6 +35,12 @@ data:
4135
{{- else}}
4236
{{- include "controller.config.defaultVolumeTemplate" . | fromYaml | toPrettyJson | nindent 4 }}
4337
{{- end }}
38+
reserved-name-patterns.txt: |-
39+
{{- if .Values.config.reservedNames }}
40+
{{- (tpl .Values.config.reservedNames $) | nindent 4 }}
41+
{{- else}}
42+
{{- include "controller.config.defaultReservedNamePatterns" . | nindent 4 }}
43+
{{- end }}
4444
volume-claim-template.json: |
4545
{{- if .Values.config.volumeClaimTemplate }}
4646
{{- (tpl .Values.config.volumeClaimTemplate $) | nindent 4 }}

rootfs/api/models/app.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
from api.utils import get_session, dict_diff
2424
from api.exceptions import AlreadyExists, DryccException, ServiceUnavailable
25-
from api.utils import CacheLock, DeployLock, generate_app_name, apply_tasks
25+
from api.utils import (
26+
CacheLock, DeployLock, generate_app_name, apply_tasks, validate_reserved_names)
2627
from scheduler import KubeHTTPException, KubeException
2728
from .gateway import Gateway, Route
2829
from .limit import LimitPlan
@@ -88,32 +89,28 @@ def validate_app_id(value):
8889
if not match:
8990
raise ValidationError("App name must start with an alphabetic character, cannot end with a"
9091
+ " hyphen and can only contain a-z (lowercase), 0-9 and hyphens.")
92+
validate_reserved_names(value)
9193

9294

9395
def validate_app_structure(value):
9496
"""Error if the dict values aren't ints >= 0"""
9597
try:
96-
if any(int(v) < 0 for v in value.values()):
97-
raise ValueError("Must be greater than or equal to zero")
98+
for k, v in value.items():
99+
if int(v) < 0:
100+
raise ValueError("Must be greater than or equal to zero")
101+
validate_reserved_names(k)
98102
except ValueError as err:
99103
raise ValidationError(str(err))
100104

101105

102-
def validate_reserved_names(value):
103-
"""A value cannot use some reserved names."""
104-
if value in settings.RESERVED_NAMES:
105-
raise ValidationError('{} is a reserved name.'.format(value))
106-
107-
108106
class App(UuidAuditedModel):
109107
"""
110108
Application used to service requests on behalf of end-users
111109
"""
112110

113111
owner = models.ForeignKey(User, on_delete=models.PROTECT)
114112
id = models.SlugField(max_length=63, unique=True, null=True,
115-
validators=[validate_app_id,
116-
validate_reserved_names])
113+
validators=[validate_app_id])
117114
structure = models.JSONField(
118115
default=dict, blank=True, validators=[validate_app_structure])
119116

rootfs/api/models/gateway.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.db.models import Q
77
from django.http import Http404
88

9+
from api.utils import validate_label
910
from api.exceptions import ServiceUnavailable
1011
from scheduler import KubeException
1112

@@ -22,7 +23,7 @@
2223
class Gateway(AuditedModel):
2324
app = models.ForeignKey('App', on_delete=models.CASCADE)
2425
owner = models.ForeignKey(User, on_delete=models.PROTECT)
25-
name = models.CharField(max_length=63, db_index=True)
26+
name = models.CharField(max_length=63, db_index=True, validators=[validate_label])
2627
ports = models.JSONField(default=list)
2728

2829
def log(self, message, level=logging.INFO):

rootfs/api/serializers/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
from api import models
18-
from api.utils import validate_json
18+
from api.utils import validate_json, validate_reserved_names
1919
from api.exceptions import DryccException
2020
from api.models.volume import Volume
2121
from api.models.base import PTYPE_MIN_LENGTH, PTYPE_MAX_LENGTH
@@ -78,6 +78,7 @@ def validate_ptype(value):
7878
raise serializers.ValidationError(
7979
"The length of ptype must be between {} and {}".format(
8080
PTYPE_MIN_LENGTH, PTYPE_MAX_LENGTH))
81+
validate_reserved_names(value)
8182
return value
8283

8384

@@ -91,6 +92,7 @@ def validate_name(value):
9192
raise serializers.ValidationError(
9293
"The length of name must be between {} and {}".format(
9394
PTYPE_MIN_LENGTH, PTYPE_MAX_LENGTH))
95+
validate_reserved_names(value)
9496
return value
9597

9698

rootfs/api/settings/production.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,13 @@ def randstr(k):
255255
TEMPDIR = tempfile.mkdtemp(prefix='drycc')
256256

257257
# names which apps cannot reserve for routing
258-
RESERVED_NAMES_PATH = os.environ.get(
259-
'RESERVED_NAMES_PATH', '/etc/controller/reserved-names.txt')
260-
if os.path.exists(RESERVED_NAMES_PATH):
261-
with open(RESERVED_NAMES_PATH) as f:
262-
RESERVED_NAMES = [line.strip() for line in f if line]
258+
RESERVED_NAME_PATTERNS_PATH = os.environ.get(
259+
'RESERVED_NAME_PATTERNS_PATH', '/etc/controller/reserved-name-patterns.txt')
260+
if os.path.exists(RESERVED_NAME_PATTERNS_PATH):
261+
with open(RESERVED_NAME_PATTERNS_PATH) as f:
262+
RESERVED_NAME_PATTERNS = [line.strip() for line in f if line.strip()]
263263
else:
264-
RESERVED_NAMES = ["drycc", "drycc-helmbroker", "drycc-manager", "kube-system", "default"]
264+
RESERVED_NAME_PATTERNS = [r"^drycc(?:-[\w-]+)?$", r"^kube(?:-[\w-]+)?$", r"^default$"]
265265

266266
# the k8s namespace in which the controller and workflow were installed.
267267
WORKFLOW_NAMESPACE = os.environ.get('WORKFLOW_NAMESPACE', 'drycc')

rootfs/api/tests/test_app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ def test_app_errors(self, mock_requests):
158158

159159
def test_app_reserved_names(self, mock_requests):
160160
"""Nobody should be able to create applications with names which are reserved."""
161-
reserved_names = ['foo', 'bar']
162-
with self.settings(RESERVED_NAMES=reserved_names):
161+
reserved_names = ['fooooo', 'barrrrrr']
162+
with self.settings(RESERVED_NAME_PATTERNS=reserved_names):
163163
for name in reserved_names:
164164
response = self.client.post('/v2/apps', {'id': name})
165165
self.assertContains(

rootfs/api/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ def validate_label(value):
7575
match = re.match(r'^[a-z0-9-]+$', value)
7676
if not match:
7777
raise ValidationError("Can only contain a-z (lowercase), 0-9 and hyphens")
78+
validate_reserved_names(value)
79+
80+
81+
def validate_reserved_names(value):
82+
"""A value cannot use some reserved names."""
83+
for reserved_name_pattern in settings.RESERVED_NAME_PATTERNS:
84+
if re.match(reserved_name_pattern, value):
85+
raise ValidationError('{} is a reserved name.'.format(value))
7886

7987

8088
def random_string(num):

rootfs/api/views.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from urllib.parse import urljoin
1616
from asgiref.sync import async_to_sync
17+
from django.db import transaction
1718
from django.db.models import Q
1819
from django.core.cache import cache
1920
from django.http import Http404, HttpResponse
@@ -348,6 +349,11 @@ def run(self, request, **kwargs):
348349
timeout=timeout, expires=expires)
349350
return Response(status=status.HTTP_204_NO_CONTENT)
350351

352+
@transaction.atomic
353+
def create(self, request, *args, **kwargs):
354+
return super().create(request, *args, **kwargs)
355+
356+
@transaction.atomic
351357
def update(self, request, **kwargs):
352358
app = self.get_object()
353359
old_owner = app.owner
@@ -361,6 +367,10 @@ def update(self, request, **kwargs):
361367
downstream_model_owner.delay(app, old_owner, new_owner)
362368
return Response(status=status.HTTP_200_OK)
363369

370+
@transaction.atomic
371+
def destroy(self, request, *args, **kwargs):
372+
return super().destroy(request, *args, **kwargs)
373+
364374

365375
class BuildViewSet(ReleasableViewSet):
366376
"""A viewset for interacting with Build objects."""
@@ -1353,7 +1363,7 @@ async def authenticate(self, request, username):
13531363
return None, {'error': 'Unauthorized', "status_code": 401}
13541364
if auth[0].username != username:
13551365
return None, {'error': 'Access denied', "status_code": 403}
1356-
return auth[0].id, None
1366+
return auth[0].id, None
13571367

13581368

13591369
@method_decorator(csrf_exempt, name='dispatch')

0 commit comments

Comments
 (0)