Skip to content

Commit e591231

Browse files
committed
chore(controller): code review
1 parent b8cdff8 commit e591231

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+247
-226
lines changed

rootfs/api/management/commands/load_db_state_to_k8s.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
from django.core.management.base import BaseCommand
33
from django.shortcuts import get_object_or_404
44

5-
from api.models import Key, App, Domain, Certificate, Service
5+
from api.models.key import Key
6+
from api.models.app import App
7+
from api.models.domain import Domain
8+
from api.models.certificate import Certificate
9+
from api.models.service import Service
610
from api.exceptions import DryccException, AlreadyExists
711

812

rootfs/api/management/commands/measure_apps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from django.utils import timezone
55
from django.core.management.base import BaseCommand
66
from django.conf import settings
7-
from api.models import App
7+
from api.models.app import App
88
from api.tasks import send_measurements
99

1010
logger = logging.getLogger(__name__)

rootfs/api/management/commands/measure_networks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.core.management.base import BaseCommand
66
from django.conf import settings
77
from api import influxdb
8-
from api.models import Config
8+
from api.models.config import Config
99
from api.tasks import send_measurements
1010

1111
logger = logging.getLogger(__name__)

rootfs/api/management/commands/measure_resources.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from django.utils import timezone
55
from django.core.management.base import BaseCommand
66
from django.conf import settings
7-
from api.models import Resource
7+
from api.models.resource import Resource
88
from api.tasks import send_measurements
99

1010
logger = logging.getLogger(__name__)

rootfs/api/management/commands/measure_volumes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from django.utils import timezone
55
from django.core.management.base import BaseCommand
66
from django.conf import settings
7-
from api.models import Volume
7+
from api.models.volume import Volume
88
from api.tasks import send_measurements
99

1010
logger = logging.getLogger(__name__)

rootfs/api/migrations/0001_initial.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Generated by Django 3.2.5 on 2021-08-09 06:50
22

3+
import api.utils
34
import api.models
45
import api.models.app
56
import api.models.certificate
@@ -63,7 +64,7 @@ class Migration(migrations.Migration):
6364
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
6465
('created', models.DateTimeField(auto_now_add=True)),
6566
('updated', models.DateTimeField(auto_now=True)),
66-
('name', models.CharField(max_length=253, unique=True, validators=[api.models.validate_label])),
67+
('name', models.CharField(max_length=253, unique=True, validators=[api.utils.validate_label])),
6768
('certificate', models.TextField(validators=[api.models.certificate.validate_certificate])),
6869
('key', models.TextField(validators=[api.models.certificate.validate_private_key])),
6970
('common_name', models.TextField(editable=False, null=True)),
@@ -124,7 +125,7 @@ class Migration(migrations.Migration):
124125
('uuid', models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='UUID')),
125126
('created', models.DateTimeField(auto_now_add=True)),
126127
('updated', models.DateTimeField(auto_now=True)),
127-
('name', models.CharField(max_length=63, validators=[api.models.validate_label])),
128+
('name', models.CharField(max_length=63, validators=[api.utils.validate_label])),
128129
('size', models.CharField(max_length=128)),
129130
('path', models.JSONField(blank=True, default=dict)),
130131
('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.app')),
@@ -176,7 +177,7 @@ class Migration(migrations.Migration):
176177
('uuid', models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='UUID')),
177178
('created', models.DateTimeField(auto_now_add=True)),
178179
('updated', models.DateTimeField(auto_now=True)),
179-
('name', models.CharField(max_length=63, validators=[api.models.validate_label])),
180+
('name', models.CharField(max_length=63, validators=[api.utils.validate_label])),
180181
('plan', models.CharField(max_length=128)),
181182
('data', models.JSONField(blank=True, default=dict)),
182183
('status', models.TextField(blank=True, null=True)),

rootfs/api/models/__init__.py

Lines changed: 30 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
"""
44
Data models for the Drycc API.
55
"""
6+
import os
67
import time
78
import hashlib
89
import hmac
9-
import importlib
1010
import logging
11-
import morph
12-
import re
1311
import urllib.parse
14-
import uuid
12+
import pkgutil
13+
import inspect
1514
import requests
1615
from datetime import timedelta
1716
from django.conf import settings
@@ -20,142 +19,37 @@
2019
from django.utils.timezone import now
2120
from django.dispatch import receiver
2221
from django.contrib.auth import get_user_model
23-
from rest_framework.exceptions import ValidationError
2422
from rest_framework.authtoken.models import Token
25-
from requests_toolbelt import user_agent
26-
from scheduler.exceptions import KubeException
27-
from .. import __version__ as drycc_version
28-
from ..exceptions import DryccException, AlreadyExists, ServiceUnavailable, UnprocessableEntity # noqa
23+
from api.utils import get_session
24+
from api.tasks import retrieve_resource, send_measurements
25+
from .app import App
26+
from .appsettings import AppSettings
27+
from .build import Build
28+
from .certificate import Certificate
29+
from .config import Config
30+
from .domain import Domain
31+
from .release import Release
32+
from .tls import TLS
33+
from .volume import Volume
34+
from .resource import Resource
35+
2936

3037
User = get_user_model()
3138
logger = logging.getLogger(__name__)
32-
session = None
33-
34-
35-
def get_session():
36-
global session
37-
if session is None:
38-
session = requests.Session()
39-
session.headers = {
40-
# https://toolbelt.readthedocs.org/en/latest/user-agent.html#user-agent-constructor
41-
'User-Agent': user_agent('Drycc Controller', drycc_version),
42-
}
43-
# `mount` a custom adapter that retries failed connections for HTTP and HTTPS requests.
44-
# http://docs.python-requests.org/en/latest/api/#requests.adapters.HTTPAdapter
45-
session.mount('http://', requests.adapters.HTTPAdapter(max_retries=10))
46-
session.mount('https://', requests.adapters.HTTPAdapter(max_retries=10))
47-
return session
48-
49-
50-
def validate_label(value):
51-
"""
52-
Check that the value follows the kubernetes name constraints
53-
http://kubernetes.io/v1.1/docs/design/identifiers.html
54-
"""
55-
match = re.match(r'^[a-z0-9-]+$', value)
56-
if not match:
57-
raise ValidationError("Can only contain a-z (lowercase), 0-9 and hyphens")
58-
59-
60-
class AuditedModel(models.Model):
61-
"""Add created and updated fields to a model."""
62-
63-
created = models.DateTimeField(auto_now_add=True)
64-
updated = models.DateTimeField(auto_now=True)
65-
66-
class Meta:
67-
"""Mark :class:`AuditedModel` as abstract."""
68-
abstract = True
69-
70-
@classmethod
71-
@property
72-
def _scheduler(cls):
73-
mod = importlib.import_module(settings.SCHEDULER_MODULE)
74-
return mod.SchedulerClient(settings.SCHEDULER_URL, settings.K8S_API_VERIFY_TLS)
75-
76-
def _fetch_service_config(self, app, svc_name=None):
77-
try:
78-
# Get the service from k8s to attach the domain correctly
79-
if svc_name is None:
80-
svc_name = app
81-
svc = self._scheduler.svc.get(app, svc_name).json()
82-
except KubeException as e:
83-
raise ServiceUnavailable('Could not fetch Kubernetes Service {}'.format(app)) from e
84-
85-
# Get minimum structure going if it is missing on the service
86-
if 'metadata' not in svc or 'annotations' not in svc['metadata']:
87-
default = {'metadata': {'annotations': {}}}
88-
svc = dict_merge(svc, default)
89-
90-
if 'labels' not in svc['metadata']:
91-
default = {'metadata': {'labels': {}}}
92-
svc = dict_merge(svc, default)
93-
94-
return svc
95-
96-
def _load_service_config(self, app, component, svc_name=None):
97-
# fetch setvice definition with minimum structure
98-
svc = self._fetch_service_config(app, svc_name)
99-
100-
# always assume a .drycc.cc/ ending
101-
component = "%s.drycc.cc/" % component
102-
103-
# Filter to only include values for the component and strip component out of it
104-
# Processes dots into a nested structure
105-
config = morph.unflatten(morph.pick(svc['metadata']['annotations'], prefix=component))
106-
107-
return config
108-
109-
def _save_service_config(self, app, component, data, svc_name=None):
110-
if svc_name is None:
111-
svc_name = app
112-
# fetch setvice definition with minimum structure
113-
svc = self._fetch_service_config(app, svc_name)
114-
115-
# always assume a .drycc.cc ending
116-
component = "%s.drycc.cc/" % component
117-
118-
# add component to data and flatten
119-
data = {"%s%s" % (component, key): value for key, value in list(data.items()) if value}
120-
svc['metadata']['annotations'].update(morph.flatten(data))
121-
122-
# Update the k8s service for the application with new service information
123-
try:
124-
self._scheduler.svc.update(app, svc_name, svc)
125-
except KubeException as e:
126-
raise ServiceUnavailable('Could not update Kubernetes Service {}'.format(app)) from e
127-
128-
129-
class UuidAuditedModel(AuditedModel):
130-
"""Add a UUID primary key to an :class:`AuditedModel`."""
131-
132-
uuid = models.UUIDField('UUID',
133-
default=uuid.uuid4,
134-
primary_key=True,
135-
editable=False,
136-
auto_created=True,
137-
unique=True)
138-
139-
class Meta:
140-
"""Mark :class:`UuidAuditedModel` as abstract."""
141-
abstract = True
142-
143-
144-
from .app import App, validate_app_id, validate_reserved_names, validate_app_structure # noqa
145-
from .appsettings import AppSettings # noqa
146-
from .blocklist import Blocklist # noqa
147-
from .build import Build # noqa
148-
from .certificate import Certificate, validate_certificate # noqa
149-
from .config import Config # noqa
150-
from .domain import Domain # noqa
151-
from .service import Service # noqa
152-
from .key import Key, validate_base64 # noqa
153-
from .release import Release # noqa
154-
from .tls import TLS # noqa
155-
from .volume import Volume # noqa
156-
from .resource import Resource # noqa
157-
from ..tasks import retrieve_resource, send_measurements # noqa
158-
from ..utils import dict_merge # noqa
39+
40+
41+
# In order to comply with the Django specification, all models need to be imported
42+
def import_all_models():
43+
for _, modname, ispkg in pkgutil.iter_modules([os.path.dirname(__file__)]):
44+
if not ispkg:
45+
exec(f"from api.models.{modname} import *")
46+
for key, value in locals().items():
47+
if inspect.isclass(value) and issubclass(value, models.Model):
48+
globals()[key] = value
49+
50+
51+
import_all_models()
52+
15953

16054
# define update/delete callbacks for synchronizing
16155
# models with the configuration management backend

rootfs/api/models/app.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@
2020
from django.contrib.auth import get_user_model
2121
from rest_framework.exceptions import ValidationError, NotFound
2222

23-
from api.models import get_session
24-
from api.models import UuidAuditedModel, AlreadyExists, DryccException, ServiceUnavailable
25-
from api.models.config import Config
26-
from api.models.domain import Domain
27-
from api.models.release import Release
28-
from api.models.tls import TLS
29-
from api.models.appsettings import AppSettings
30-
from api.models.volume import Volume
23+
from api.utils import get_session
24+
from api.exceptions import AlreadyExists, DryccException, ServiceUnavailable
3125
from api.utils import generate_app_name, apply_tasks, unit_to_bytes, unit_to_millicpu
3226
from scheduler import KubeHTTPException, KubeException
27+
from .config import Config
28+
from .domain import Domain
29+
from .release import Release
30+
from .tls import TLS
31+
from .appsettings import AppSettings
32+
from .volume import Volume
33+
from .base import UuidAuditedModel
3334

3435
User = get_user_model()
3536
logger = logging.getLogger(__name__)

rootfs/api/models/appsettings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from rest_framework.exceptions import NotFound
66
from django.contrib.auth import get_user_model
77
from api.utils import dict_diff
8-
from api.models import UuidAuditedModel
98
from api.exceptions import DryccException, AlreadyExists, UnprocessableEntity
9+
from .base import UuidAuditedModel
1010

1111
User = get_user_model()
1212

0 commit comments

Comments
 (0)