Skip to content

Commit 600670f

Browse files
committed
feat(ingress): Improving the configuration of ingress
1 parent 8360e26 commit 600670f

10 files changed

Lines changed: 256 additions & 42 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{{ if .Values.cert_manager_enabled }}
2+
apiVersion: certmanager.k8s.io/v1alpha1
3+
kind: Certificate
4+
metadata:
5+
name: drycc-controller
6+
namespace: "{{ .Release.Namespace }}"
7+
spec:
8+
secretName: drycc-controller-auto-tls
9+
issuerRef:
10+
name: drycc-letsencrypt
11+
kind: ClusterIssuer
12+
dnsNames:
13+
- drycc.{{ .Values.platform_domain }}
14+
acme:
15+
config:
16+
- http01:
17+
ingressClass: "{{ .Values.global.ingress_class }}"
18+
domains:
19+
- drycc.{{ .Values.platform_domain }}
20+
{{- end }}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: certmanager.k8s.io/v1alpha1
2+
kind: ClusterIssuer
3+
metadata:
4+
name: drycc-letsencrypt
5+
spec:
6+
acme:
7+
# The ACME server URL
8+
server: https://acme-v02.api.letsencrypt.org/directory
9+
# Email address used for ACME registration
10+
email: "{{ .Values.global.email }}"
11+
# Name of a secret used to store the ACME account private key
12+
privateKeySecretRef:
13+
name: drycc-letsencrypt
14+
# Enable HTTP01 validations
15+
http01: {}

charts/controller/templates/controller-ingress.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
{{ if not (eq .Values.global.ingress_class "") }}
21
apiVersion: extensions/v1beta1
32
kind: Ingress
43
metadata:
@@ -25,4 +24,9 @@ spec:
2524
backend:
2625
serviceName: drycc-controller
2726
servicePort: 80
28-
{{- end }}
27+
{{ if .Values.cert_manager_enabled }}
28+
tls:
29+
- secretName: drycc-controller-auto-tls
30+
hosts:
31+
- drycc.{{ .Values.platform_domain }}
32+
{{- end }}

charts/controller/values.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ k8s_api_verify_tls: "true"
1919
#
2020
# This will be the hostname that is used to build endpoints such as "drycc.$HOSTNAME"
2121
platform_domain: ""
22+
# Whether cert_manager is enabled to automatically generate controller certificates
23+
cert_manager_enabled: "true"
2224

2325
global:
26+
# Admin email, used for each component to send email to administrator
27+
email: "drycc@drycc.cc"
2428
# Set the storage backend
2529
#
2630
# Valid values are:
@@ -52,8 +56,5 @@ global:
5256
secret_prefix: "private-registry"
5357
# Role-Based Access Control for Kubernetes >= 1.5
5458
use_rbac: false
55-
# Experimental feature to use Kubernetes ingress instead of Workflow's drycc-router.
56-
# If ingress_class is empty, use drycc-router
57-
# Otherwise, please check `kubernetes.io/ingress.class`
58-
# The cert-manager component must be installed
59+
# Please check `kubernetes.io/ingress.class`
5960
ingress_class: ""

rootfs/api/models/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,10 @@ def create(self, *args, **kwargs): # noqa
237237
self._scheduler.ingress.get(ingress)
238238
except KubeException:
239239
self.log("creating Ingress {}".format(namespace), level=logging.INFO)
240+
host = "%s.%s" % (ingress, settings.PLATFORM_DOMAIN)
240241
self._scheduler.ingress.create(
241242
ingress, settings.INGRESS_CLASS, namespace,
242-
settings.PLATFORM_DOMAIN)
243+
hosts=[host, ], tls=[])
243244
except KubeException as e:
244245
raise ServiceUnavailable('Could not create Ingress in Kubernetes') from e
245246
try:

rootfs/api/settings/production.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@
262262

263263
# experimental native ingress
264264
INGRESS_CLASS = os.environ.get('DRYCC_INGRESS_CLASS', '')
265+
265266
PLATFORM_DOMAIN = os.environ.get('DRYCC_PLATFORM_DOMAIN', '')
266267

267268
# k8s image policies
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import json
2+
from scheduler.resources import Resource
3+
from scheduler.exceptions import KubeHTTPException, KubeException
4+
5+
6+
class Certificate(Resource):
7+
8+
def manifest(self, namespace, name, ingress_class, hosts):
9+
data = {
10+
"apiVersion": "certmanager.k8s.io/v1alpha1",
11+
"kind": "Certificate",
12+
"metadata": {
13+
"name": name,
14+
"namespace": namespace
15+
},
16+
"spec": {
17+
"secretName": "%s-auto-tls" % name,
18+
"issuerRef": {
19+
"name": "drycc-controller-letsencrypt",
20+
"kind": "ClusterIssuer"
21+
},
22+
"dnsNames": hosts,
23+
"acme": {
24+
"config": [
25+
{
26+
"http01": {
27+
"ingressClass": ingress_class
28+
},
29+
"domains": hosts
30+
}
31+
]
32+
}
33+
}
34+
}
35+
return data
36+
37+
def get(self, namespace, name):
38+
"""
39+
Fetch a single Ingress or a list of Ingresses
40+
"""
41+
if name is not None:
42+
url = "/apis/certmanager/v1alpha1/namespaces/%s/certificates/%s" % (namespace, name)
43+
message = 'get Ingress ' + name
44+
else:
45+
url = "/apis/certmanager/v1alpha1/namespaces/%s/certificates" % namespace
46+
message = 'get Ingresses'
47+
48+
def create(self, namespace, name, ingress_class, hosts):
49+
pass
50+
51+
def put(self, namespace, name, ingress_class, hosts):
52+
pass
53+
54+
def delete(self, namespace, name):
55+
pass
Lines changed: 98 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,95 @@
11
from scheduler.exceptions import KubeHTTPException
22
from scheduler.resources import Resource
33

4+
MANIFEAT_CLASSES = {}
5+
6+
7+
class BaseManifest(object):
8+
9+
def manifest(self, ingress, ingress_class, namespace, **kwargs):
10+
path = "/*" if ingress_class in ("gce", "alb") else "/"
11+
hosts, tls = kwargs.pop("hosts"), kwargs.pop("tls")
12+
data = {
13+
"kind": "Ingress",
14+
"apiVersion": "extensions/v1beta1",
15+
"metadata": {
16+
"name": ingress,
17+
"annotations": {
18+
"kubernetes.io/tls-acme": "true",
19+
"kubernetes.io/ingress.class": ingress_class
20+
}
21+
},
22+
"spec": {
23+
"rules": [{
24+
"host": host,
25+
"http": {
26+
"paths": [
27+
{
28+
"path": path,
29+
"backend": {
30+
"serviceName": ingress,
31+
"servicePort": 80
32+
}
33+
}
34+
]
35+
}
36+
} for host in hosts],
37+
"tls": tls
38+
}
39+
}
40+
return data
41+
MANIFEAT_CLASSES["default"] = BaseManifest
42+
43+
44+
class NginxManifest(BaseManifest):
45+
46+
def manifest(self, ingress, ingress_class, namespace, **kwargs):
47+
data = BaseManifest.manifest(
48+
self, ingress, ingress_class, namespace, **kwargs)
49+
if "whitelist" in kwargs:
50+
whitelist = ", ".join(kwargs.pop("whitelist"))
51+
data.update({
52+
"nginx.ingress.kubernetes.io/whitelist-source-range": whitelist
53+
})
54+
if "ssl_redirect" in kwargs:
55+
ssl_redirect = kwargs.pop("ssl_redirect")
56+
data.update({
57+
"nginx.ingress.kubernetes.io/ssl-redirect": ssl_redirect
58+
})
59+
return data
60+
MANIFEAT_CLASSES["nginx"] = NginxManifest
61+
62+
63+
class TraefikManifest(BaseManifest):
64+
65+
def manifest(self, ingress, ingress_class, namespace, **kwargs):
66+
data = BaseManifest.manifest(
67+
self, ingress, ingress_class, namespace, **kwargs)
68+
if "whitelist" in kwargs:
69+
whitelist = ", ".join(kwargs.pop("whitelist"))
70+
data.update({
71+
"ingress.kubernetes.io/whitelist-x-forwarded-for": "true",
72+
"traefik.ingress.kubernetes.io/whitelist-source-range": whitelist
73+
})
74+
if "ssl_redirect" in kwargs:
75+
ssl_redirect = kwargs.pop("ssl_redirect")
76+
data.update({
77+
"ingress.kubernetes.io/ssl-redirect": ssl_redirect
78+
})
79+
return data
80+
MANIFEAT_CLASSES["traefik"] = TraefikManifest
81+
482

583
class Ingress(Resource):
684
short_name = 'ingress'
785

86+
def manifest(self, ingress, ingress_class, namespace, **kwargs):
87+
if ingress_class not in MANIFEAT_CLASSES:
88+
ingress_class = "default"
89+
return MANIFEAT_CLASSES.get(ingress_class)().manifest(
90+
ingress, ingress_class, namespace, **kwargs
91+
)
92+
893
def get(self, name=None, **kwargs):
994
"""
1095
Fetch a single Ingress or a list of Ingresses
@@ -22,51 +107,30 @@ def get(self, name=None, **kwargs):
22107

23108
return response
24109

25-
def create(self, ingress, ingress_class, namespace, hostname):
110+
def create(self, ingress, ingress_class, namespace, **kwargs):
26111
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses" % namespace
27-
path = "/*" if ingress_class in ("gce", "alb") else "/"
28-
data = {
29-
"kind": "Ingress",
30-
"apiVersion": "extensions/v1beta1",
31-
"metadata": {
32-
"name": ingress,
33-
"annotations": {
34-
"kubernetes.io/tls-acme": "true"
35-
}
36-
},
37-
"spec": {
38-
"rules": [
39-
{
40-
"host": ingress + "." + hostname,
41-
"http": {
42-
"paths": [
43-
{
44-
"path": path,
45-
"backend": {
46-
"serviceName": ingress,
47-
"servicePort": 80
48-
}
49-
}
50-
]
51-
}
52-
}
53-
]
54-
}
55-
}
56-
if ingress_class:
57-
data["metadata"]["annotations"].update({
58-
"kubernetes.io/ingress.class": ingress_class})
112+
data = self.manifest(ingress, ingress_class, namespace, **kwargs)
59113
response = self.http_post(url, json=data)
60114

61115
if not response.status_code == 201:
62116
raise KubeHTTPException(response, "create Ingress {}".format(namespace))
63117

64118
return response
65119

120+
def update(self, ingress, ingress_class, namespace, **kwargs):
121+
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses/%s" % (namespace, ingress)
122+
data = self.manifest(ingress, ingress_class, namespace, **kwargs)
123+
response = self.http_put(url, json=data)
124+
125+
if self.unhealthy(response.status_code):
126+
raise KubeHTTPException(response, "update Ingress {}".format(namespace))
127+
128+
return response
129+
66130
def delete(self, namespace, ingress):
67131
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses/%s" % (namespace, ingress)
68132
response = self.http_delete(url)
69133
if self.unhealthy(response.status_code):
70134
raise KubeHTTPException(response, 'delete Ingress "{}"', namespace)
71135

72-
return response
136+
return response

rootfs/scheduler/resources/secret.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,55 @@ def delete(self, namespace, name):
113113
)
114114

115115
return response
116+
117+
118+
class Certificate(Resource):
119+
120+
def manifest(self, namespace, name, ingress_class, hosts):
121+
data = {
122+
"apiVersion": "certmanager.k8s.io/v1alpha1",
123+
"kind": "Certificate",
124+
"metadata": {
125+
"name": name,
126+
"namespace": namespace
127+
},
128+
"spec": {
129+
"secretName": "%s-auto-tls" % name,
130+
"issuerRef": {
131+
"name": "drycc-controller-letsencrypt",
132+
"kind": "ClusterIssuer"
133+
},
134+
"dnsNames": hosts,
135+
"acme": {
136+
"config": [
137+
{
138+
"http01": {
139+
"ingressClass": ingress_class
140+
},
141+
"domains": hosts
142+
}
143+
]
144+
}
145+
}
146+
}
147+
return data
148+
149+
def get(self, namespace, name):
150+
"""
151+
Fetch a single Ingress or a list of Ingresses
152+
"""
153+
if name is not None:
154+
url = "/apis/certmanager/v1alpha1/namespaces/%s/certificates/%s" % (namespace, name)
155+
message = 'get Ingress ' + name
156+
else:
157+
url = "/apis/certmanager/v1alpha1/namespaces/%s/certificates" % namespace
158+
message = 'get Ingresses'
159+
160+
def create(self, namespace, name, ingress_class, hosts):
161+
pass
162+
163+
def put(self, namespace, name, ingress_class, hosts):
164+
pass
165+
166+
def delete(self, namespace, name):
167+
pass

rootfs/scheduler/tests/test_ingress.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class IngressTest(TestCase):
1313
def test_create_ingress(self):
1414
# Ingress assumes that the namespace and ingress name are always the same
1515
self.scheduler.ns.create("test-ingress")
16-
self.scheduler.ingress.create("test-ingress", "nginx", "test-ingress", "test-ingress")
16+
self.scheduler.ingress.create("test-ingress", "nginx", "test-ingress",
17+
hosts=["test-ingress"], tls=[])
1718

1819
def test_get_ingresses(self):
1920
response = self.scheduler.ingress.get()

0 commit comments

Comments
 (0)