Skip to content

Commit 6d75b6d

Browse files
committed
fix(certificate): failed to create certificate
1 parent 60cf663 commit 6d75b6d

10 files changed

Lines changed: 97 additions & 74 deletions

File tree

rootfs/api/models/app.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
from api.exceptions import AlreadyExists, DryccException, ServiceUnavailable
2727
from api.utils import generate_app_name, apply_tasks, unit_to_bytes, unit_to_millicpu
2828
from scheduler import KubeHTTPException, KubeException
29-
from .gateway import Gateway, Route
29+
from scheduler.resources.pod import DEFAULT_CONTAINER_PORT
30+
from .gateway import Gateway, Route, DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT
3031
from .config import Config
3132
from .service import Service
3233
from .release import Release
@@ -37,6 +38,7 @@
3738

3839
User = get_user_model()
3940
logger = logging.getLogger(__name__)
41+
DEFAULT_PROCFILE_TYPE = "web"
4042

4143

4244
# http://kubernetes.io/v1.1/docs/design/identifiers.html
@@ -297,7 +299,7 @@ def deploy(self, release, force_deploy=False, rollback_on_failure=True): # noqa
297299
structure = self.structure.copy()
298300
# zero out canonical pod counts
299301
for proctype in structure.keys():
300-
if proctype == "web":
302+
if proctype == DEFAULT_PROCFILE_TYPE:
301303
structure[proctype] = 0
302304
# update with the default process type.
303305
structure.update(self._default_structure(release))
@@ -371,15 +373,15 @@ def deploy(self, release, force_deploy=False, rollback_on_failure=True): # noqa
371373
self.log(err, logging.ERROR)
372374
raise ServiceUnavailable(err) from e
373375
for procfile_type, value in deploys.items():
374-
if procfile_type == "web": # http
375-
target_port = int(value.get('envs', {}).get('PORT', 5000))
376-
self._create_default_ingress(procfile_type, target_port)
376+
if procfile_type == DEFAULT_PROCFILE_TYPE: # http
377+
target_port = int(value.get('envs', {}).get('PORT', DEFAULT_CONTAINER_PORT))
378+
self._create_default_ingress(target_port)
377379
service = self.service_set.filter(procfile_type=procfile_type).first()
378380
if not service:
379381
continue
380382
if prev_release and prev_release.build:
381383
continue
382-
if procfile_type == "web":
384+
if procfile_type == DEFAULT_PROCFILE_TYPE:
383385
self._verify_http_health(service, **deploys[procfile_type])
384386
else:
385387
self._verify_tcp_health(service, **deploys[procfile_type])
@@ -816,35 +818,36 @@ def _set_default_config(self):
816818
config.memory = new_memory
817819
config.save()
818820

819-
def _create_default_ingress(self, procfile_type, target_port):
820-
port = 80
821+
def _create_default_ingress(self, target_port):
821822
# create default service
822823
try:
823-
service = self.service_set.filter(procfile_type=procfile_type).latest()
824+
service = self.service_set.filter(procfile_type=DEFAULT_PROCFILE_TYPE).latest()
824825
except Service.DoesNotExist:
825-
service = Service(owner=self.owner, app=self, procfile_type=procfile_type)
826-
service.add_port(port, "TCP", target_port)
826+
service = Service(owner=self.owner, app=self, procfile_type=DEFAULT_PROCFILE_TYPE)
827+
service.add_port(DEFAULT_HTTP_PORT, "TCP", target_port)
827828
service.save()
828829
else:
829-
if service.update_port(port, "TCP", target_port):
830+
if service.update_port(DEFAULT_HTTP_PORT, "TCP", target_port):
830831
service.save()
831832
# create default gateway
832833
try:
833834
gateway = self.gateway_set.filter(name=self.id).latest()
834835
except Gateway.DoesNotExist:
835836
gateway = Gateway(app=self, owner=self.owner, name=self.id)
836-
added, msg = gateway.add(port, "HTTP")
837-
if not added:
838-
raise DryccException(msg)
837+
modified = gateway.add(DEFAULT_HTTP_PORT, "HTTP")
838+
if self.tls_set.latest().certs_auto_enabled or self.domain_set.filter(
839+
models.Q(certificate__isnull=False)).exists():
840+
modified = gateway.add(DEFAULT_HTTPS_PORT, "HTTPS") if not modified else True
841+
if modified:
839842
gateway.save()
840843
# create default route
841844
try:
842845
self.route_set.filter(name=self.id).latest()
843846
except Route.DoesNotExist:
844847
route = Route(app=self, owner=self.owner, kind="HTTPRoute", name=self.id,
845-
port=port, procfile_type=service.procfile_type)
848+
port=DEFAULT_HTTP_PORT, procfile_type=service.procfile_type)
846849
route.rules = route.default_rules
847-
attached, msg = route.attach(gateway.name, port)
850+
attached, msg = route.attach(gateway.name, DEFAULT_HTTP_PORT)
848851
if not attached:
849852
raise DryccException(msg)
850853
route.save()
@@ -943,11 +946,11 @@ def _check_deployment_in_progress(self, deploys, release, force_deploy=False):
943946
def _default_structure(release):
944947
"""Scale to default structure based on release type"""
945948
if release.build.sha and not release.build.dockerfile and \
946-
(release.build.procfile and 'web' not in release.build.procfile):
949+
(release.build.procfile and DEFAULT_PROCFILE_TYPE not in release.build.procfile):
947950
structure = {}
948951
# default to heroku workflow
949952
else:
950-
structure = {'web': 1}
953+
structure = {DEFAULT_PROCFILE_TYPE: 1}
951954
return structure
952955

953956
def _scheduler_filter(self, **kwargs):
@@ -1119,7 +1122,8 @@ def _gather_app_settings(self, release, app_settings, process_type, replicas, vo
11191122

11201123
# only web is routable
11211124
# https://www.drycc.cc/applications/managing-app-processes/#default-process-types
1122-
routable = True if process_type == 'web' and app_settings.routable else False
1125+
routable = True if (
1126+
process_type == DEFAULT_PROCFILE_TYPE and app_settings.routable) else False
11231127

11241128
healthcheck = config.healthcheck.get(process_type, {})
11251129
volumes, volume_mounts = self._get_volumes_and_mounts(process_type, volumes)

rootfs/api/models/gateway.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
User = get_user_model()
1414
logger = logging.getLogger(__name__)
15+
DEFAULT_HTTP_PORT = 80
16+
DEFAULT_HTTPS_PORT = 443
1517

1618

1719
class Gateway(AuditedModel):
@@ -69,8 +71,10 @@ def listeners(self):
6971
port, protocol = item["port"], item["protocol"]
7072
if item["protocol"] in ("TLS", "HTTPS"):
7173
for domain in domains:
72-
secret_name = (f"{self.app.id}-auto-tls" if
73-
auto_tls else domain.certificate.name)
74+
secret_name = f"{self.app.id}-auto-tls" if auto_tls else (
75+
domain.certificate.name if domain.certificate else None)
76+
if secret_name is None:
77+
continue
7478
listeners.append({
7579
"allowedRoutes": {"namespaces": {"from": "All"}},
7680
"name": self._get_listener_name(port, protocol, domains.index(domain)),
@@ -316,7 +320,9 @@ def _https_enforced_to_k8s(self, parent_refs):
316320
rules = {
317321
"filters": [{
318322
"type": "RequestRedirect",
319-
"requestRedirect": {"port": 443, "scheme": "https", "statusCode": 301}
323+
"requestRedirect": {
324+
"port": DEFAULT_HTTPS_PORT, "scheme": "https", "statusCode": 301
325+
}
320326
}]
321327
}
322328
try:
@@ -356,7 +362,7 @@ def _get_all_parent_refs(self):
356362
"name": gateway_name,
357363
"sectionName": listener["name"],
358364
}
359-
if listener["protocol"] == "HTTP" and listener["port"] == 80:
365+
if listener["protocol"] == "HTTP" and listener["port"] == DEFAULT_HTTP_PORT:
360366
http_parent_refs.append(parent_ref)
361367
else:
362368
parent_refs.append(parent_ref)

rootfs/api/models/release.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from api.utils import dict_diff
77
from api.exceptions import DryccException, AlreadyExists
88
from scheduler import KubeHTTPException
9+
from scheduler.resources.pod import DEFAULT_CONTAINER_PORT
910
from .base import UuidAuditedModel
1011

1112
User = get_user_model()
@@ -68,8 +69,9 @@ def get_port(self):
6869
creds = self.get_registry_auth()
6970

7071
if self.build.type == "buildpack":
71-
self.app.log('buildpack type detected. Defaulting to $PORT 5000')
72-
return 5000
72+
self.app.log(
73+
'buildpack type detected. Defaulting to $PORT %s' % DEFAULT_CONTAINER_PORT)
74+
return DEFAULT_CONTAINER_PORT
7375

7476
# application has registry auth - $PORT is required
7577
if (creds is not None) or (settings.REGISTRY_LOCATION != 'on-cluster'):
@@ -85,7 +87,7 @@ def get_port(self):
8587
return int(envs.get('PORT'))
8688

8789
# If the user provides PORT
88-
return int(envs.get('PORT', 5000))
90+
return int(envs.get('PORT', DEFAULT_CONTAINER_PORT))
8991

9092
except Exception as e:
9193
raise DryccException(str(e)) from e

rootfs/api/models/tls.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,18 @@ def refresh_certificate_to_k8s(self):
102102
namespace = name = self.app.id
103103
if self.certs_auto_enabled:
104104
hosts = [domain.domain for domain in self.app.domain_set.all()]
105-
try:
106-
data = self.scheduler().certificate.get(namespace, name).json()
107-
version = data["metadata"]["resourceVersion"]
108-
self.scheduler().certificate.put(namespace, name, hosts, version)
109-
except KubeException:
110-
logger.log(
111-
msg="certificate {} does not exist".format(namespace), level=logging.INFO)
112-
self.scheduler().certificate.create(namespace, name, hosts)
105+
if len(hosts) > 0:
106+
response = self.scheduler().certificate.get(namespace, name)
107+
if response.status_code == 200:
108+
data = response.json()
109+
version = data["metadata"]["resourceVersion"]
110+
self.scheduler().certificate.put(namespace, name, hosts, version)
111+
else:
112+
logger.log(
113+
msg="certificate {} does not exist".format(namespace), level=logging.INFO)
114+
self.scheduler().certificate.create(namespace, name, hosts)
115+
else:
116+
self.app.log("skip creating certificate, no domain name set", logging.WARNING)
113117
else:
114118
self.scheduler().certificate.delete(namespace, name, ignore_exception=True)
115119

rootfs/api/serializers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from api.schemas.volumes import SCHEMA as VOLUMES_SCHEMA
2020
from api.schemas.autoscale import SCHEMA as AUTOSCALE_SCHEMA
2121
from api.schemas.healthcheck import SCHEMA as HEALTHCHECK_SCHEMA
22+
from scheduler.resources.pod import DEFAULT_CONTAINER_PORT
2223

2324

2425
User = get_user_model()
@@ -424,9 +425,9 @@ class ServiceSerializer(serializers.ModelSerializer):
424425

425426
app = serializers.SlugRelatedField(slug_field='id', queryset=models.app.App.objects.all())
426427
owner = serializers.ReadOnlyField(source='owner.username')
427-
port = serializers.IntegerField(default=5000)
428-
protocol = serializers.CharField(default="TCP")
429-
target_port = serializers.IntegerField(default=5000)
428+
port = serializers.IntegerField(required=True)
429+
protocol = serializers.CharField(required=True)
430+
target_port = serializers.IntegerField(default=DEFAULT_CONTAINER_PORT)
430431
procfile_type = serializers.CharField(required=True)
431432

432433
class Meta:

rootfs/api/signals.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from api.tasks import retrieve_resource, send_measurements
2121
from api.models.app import App
2222
from api.models.service import Service
23-
from api.models.gateway import Gateway
23+
from api.models.gateway import Gateway, DEFAULT_HTTPS_PORT
2424
from api.models.appsettings import AppSettings
2525
from api.models.build import Build
2626
from api.models.certificate import Certificate
@@ -159,7 +159,11 @@ def tls_changed_handle(sender, instance: TLS, created=False, update_fields=None,
159159
if (update_fields and "certs_auto_enabled" in update_fields) or created:
160160
instance.refresh_certificate_to_k8s()
161161
for gateway in instance.app.gateway_set.all():
162-
gateway.refresh_to_k8s()
162+
if (instance.certs_auto_enabled and gateway.name == instance.app.id
163+
and gateway.add(DEFAULT_HTTPS_PORT, "HTTPS")[0]):
164+
gateway.save()
165+
else:
166+
gateway.refresh_to_k8s()
163167
for route in instance.app.route_set.all():
164168
route.refresh_to_k8s()
165169

rootfs/api/tests/test_gateway.py

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,23 @@ def test_create_gateway(self):
7979

8080
def test_add_listener(self):
8181
app_id = self.create_app()
82-
self.create_gateway(app_id, 'bing-gateway', 8000, "HTTP")
82+
gateway_name = 'bing-gateway'
83+
self.create_gateway(app_id, gateway_name, 8000, "HTTP")
8384
response = self.client.post(
8485
'/v2/apps/{}/gateways/'.format(app_id),
85-
{'name': 'bing-gateway', 'port': 443, 'protocol': "HTTP"}
86+
{'name': gateway_name, 'port': 443, 'protocol': "HTTP"}
8687
)
8788
self.assertEqual(response.status_code, 201)
8889
response = self.client.get('/v2/apps/{}/gateways/'.format(app_id))
89-
results = [{
90+
actual = None
91+
for result in response.data["results"]:
92+
if result["name"] == gateway_name:
93+
actual = json.loads(json.dumps(result))
94+
break
95+
expect = {
9096
"app": app_id,
9197
"owner": "autotest",
92-
"name": "bing-gateway",
98+
"name": gateway_name,
9399
"listeners": [
94100
{
95101
"name": "tcp-8000-%s" % 0,
@@ -102,16 +108,17 @@ def test_add_listener(self):
102108
"port": 443,
103109
"protocol": "HTTP",
104110
"allowedRoutes": {"namespaces": {"from": "All"}}
105-
}],
111+
}
112+
],
106113
"addresses": [{
107114
"type": "IPAddress",
108115
"value": "172.22.108.207"
109116
}]
110-
}]
111-
self.assertEqual(results, json.loads(json.dumps(response.data["results"])))
117+
}
118+
self.assertEqual(expect, actual)
112119

113120
def add_tls_listener(self, name, protocol):
114-
app_id = self.create_app()
121+
app_id = self.create_app(name)
115122
domain, secret_name = self.create_tls_domain(app_id)
116123
response = self.client.post(
117124
'/v2/apps/{}/gateways/'.format(app_id),
@@ -127,27 +134,17 @@ def add_tls_listener(self, name, protocol):
127134
"app": app_id,
128135
"owner": "autotest",
129136
"name": name,
130-
"listeners": [{
131-
"tls": {
132-
"certificateRefs": [{
133-
"kind": "Secret",
134-
"name": secret_name
135-
}]
136-
},
137-
"name": listener_name,
138-
"port": 443,
139-
"hostname": domain,
140-
"protocol": protocol,
141-
"allowedRoutes": {
142-
"namespaces": {
143-
"from": "All"
144-
}
137+
"listeners": [
138+
{
139+
"tls": {"certificateRefs": [{"kind": "Secret", "name": secret_name}]},
140+
"name": listener_name,
141+
"port": 443,
142+
"hostname": domain,
143+
"protocol": protocol,
144+
"allowedRoutes": {"namespaces": {"from": "All"}}
145145
}
146-
}],
147-
"addresses": [{
148-
"type": "IPAddress",
149-
"value": "172.22.108.207"
150-
}]
146+
],
147+
"addresses": [{"type": "IPAddress", "value": "172.22.108.207"}]
151148
}]
152149
self.assertEqual(results, json.loads(json.dumps(response.data["results"])))
153150
return app_id, domain, secret_name
@@ -176,7 +173,6 @@ def test_remove_tls(self):
176173
'{}/{}/domain/{}/'.format('/v2/certs', secret_name, domain)
177174
)
178175
self.assertEqual(response.status_code, 204)
179-
180176
response = self.client.get('/v2/apps/{}/gateways/'.format(app_id))
181177
self.assertEqual(response.data["results"][0]["listeners"], [])
182178
return app_id

rootfs/api/tests/test_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def test_service_basic_ops(self):
157157
)
158158
self.assertEqual(response.status_code, 204, response.data)
159159
response = self.client.get('/v2/apps/{}/services'.format(app_id))
160-
self.assertEqual(response.data["services"], [])
160+
self.assertEqual(len(response.data["services"]), 0)
161161
# delete non-existing (1st again)
162162
response = self.client.delete(
163163
'/v2/apps/{}/services'.format(app_id),

rootfs/scheduler/resources/cert_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def manifest(api_version, namespace, name, hosts, version=None):
123123
data["metadata"]["resourceVersion"] = version
124124
return data
125125

126-
def get(self, namespace, name=None, **kwargs):
126+
def get(self, namespace, name=None, ignore_exception=True, **kwargs):
127127
"""
128128
Fetch a single certificate or a list of certificates
129129
"""
@@ -134,7 +134,7 @@ def get(self, namespace, name=None, **kwargs):
134134
url = self.api('/namespaces/{}/certificates', namespace)
135135
message = 'get certificates'
136136
response = self.http_get(url)
137-
if self.unhealthy(response.status_code):
137+
if not ignore_exception and self.unhealthy(response.status_code):
138138
raise KubeHTTPException(response, message)
139139

140140
return response

0 commit comments

Comments
 (0)