Skip to content

Commit 17120b7

Browse files
authored
Merge pull request #1274 from mboersma/byo-ingress
feat(ingress): experimental native ingress
2 parents 70e99f4 + 2dce7cd commit 17120b7

6 files changed

Lines changed: 119 additions & 1 deletion

File tree

charts/controller/templates/controller-deployment.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ spec:
5858
# NOTE(bacongobbler): use deis/registry_proxy to work around Docker --insecure-registry requirements
5959
- name: "DEIS_REGISTRY_SERVICE_HOST"
6060
value: "127.0.0.1"
61+
# Environmental variable value for $EXPERIMENTAL_NATIVE_INGRESS
62+
- name: "EXPERIMENTAL_NATIVE_INGRESS"
63+
value: "{{ .Values.global.experimental_native_ingress }}"
64+
- name: "EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME"
65+
value: "{{ .Values.platform_domain }}"
6166
- name: "K8S_API_VERIFY_TLS"
6267
value: "{{ .Values.k8s_api_verify_tls }}"
6368
- name: "DEIS_REGISTRY_SERVICE_PORT"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{{ if .Values.global.experimental_native_ingress }}
2+
apiVersion: extensions/v1beta1
3+
kind: Ingress
4+
metadata:
5+
namespace: "deis"
6+
name: "controller-api-server-ingress-http"
7+
labels:
8+
app: "controller"
9+
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
10+
release: "{{ .Release.Name }}"
11+
heritage: "{{ .Release.Service }}"
12+
spec:
13+
rules:
14+
- host: deis.{{ .Values.platform_domain }}
15+
http:
16+
paths:
17+
- path: /
18+
backend:
19+
serviceName: deis-controller
20+
servicePort: 80
21+
{{- end }}

charts/controller/values.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ deploy_hook_urls: ""
1414
registration_mode: "admin_only"
1515
# Option to disable ssl verification to connect to k8s api server
1616
k8s_api_verify_tls: "true"
17+
# The public resolvable hostname to build your cluster with.
18+
#
19+
# This will be the hostname that is used to build endpoints such as "deis.$HOSTNAME"
20+
platform_domain: ""
1721

1822
global:
1923
# Set the storage backend
@@ -45,3 +49,9 @@ global:
4549
host_port: 5555
4650
# Prefix for the imagepull secret created when using private registry
4751
secret_prefix: "private-registry"
52+
# Experimental feature to toggle using kubernetes ingress instead of the Deis router.
53+
#
54+
# Valid values are:
55+
# - true: The deis controller will now create Kubernetes ingress rules for each app, and ingress rules will automatically be created for the controller itself.
56+
# - false: The default mode, and the default behavior of Deis workflow.
57+
experimental_native_ingress: false

rootfs/api/models/app.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def create(self, *args, **kwargs): # noqa
191191

192192
# create required minimum resources in k8s for the application
193193
namespace = self.id
194+
ingress = self.id
194195
service = self.id
195196
quota_name = '{}-quota'.format(self.id)
196197
try:
@@ -200,7 +201,6 @@ def create(self, *args, **kwargs): # noqa
200201
self._scheduler.ns.get(namespace)
201202
except KubeException:
202203
self._scheduler.ns.create(namespace)
203-
204204
if settings.KUBERNETES_NAMESPACE_DEFAULT_QUOTA_SPEC != '':
205205
quota_spec = json.loads(settings.KUBERNETES_NAMESPACE_DEFAULT_QUOTA_SPEC)
206206
self.log('creating Quota {} for namespace {}'.format(quota_name, namespace),
@@ -224,6 +224,20 @@ def create(self, *args, **kwargs): # noqa
224224

225225
raise ServiceUnavailable('Kubernetes resources could not be created') from e
226226

227+
try:
228+
# In order to create an ingress, we must first have a namespace.
229+
if settings.EXPERIMENTAL_NATIVE_INGRESS:
230+
if ingress == "":
231+
raise ServiceUnavailable('Empty hostname')
232+
try:
233+
self._scheduler.ingress.get(ingress)
234+
except KubeException:
235+
self.log("creating Ingress {}".format(namespace), level=logging.INFO)
236+
self._scheduler.ingress.create(ingress,
237+
namespace,
238+
settings.EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME)
239+
except KubeException as e:
240+
raise ServiceUnavailable('Could not create Ingress in Kubernetes') from e
227241
try:
228242
self.appsettings_set.latest()
229243
except AppSettings.DoesNotExist:

rootfs/api/settings/production.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,11 @@
260260
SECRET_KEY = os.environ.get('DEIS_SECRET_KEY', random_secret)
261261
BUILDER_KEY = os.environ.get('DEIS_BUILDER_KEY', random_secret)
262262

263+
# experimental native ingress
264+
EXPERIMENTAL_NATIVE_INGRESS = bool(strtobool(
265+
os.environ.get('EXPERIMENTAL_NATIVE_INGRESS', 'false')))
266+
EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME = os.environ.get('EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME', '')
267+
263268
# k8s image policies
264269
SLUGRUNNER_IMAGE = os.environ.get('SLUGRUNNER_IMAGE_NAME', 'quay.io/deisci/slugrunner:canary') # noqa
265270
IMAGE_PULL_POLICY = os.environ.get('IMAGE_PULL_POLICY', "IfNotPresent") # noqa
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from scheduler.exceptions import KubeHTTPException
2+
from scheduler.resources import Resource
3+
4+
5+
class Ingress(Resource):
6+
short_name = 'ingress'
7+
8+
def get(self, name=None, **kwargs):
9+
"""
10+
Fetch a single Ingress or a list of Ingresses
11+
"""
12+
if name is not None:
13+
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses/%s" % (name, name)
14+
message = 'get Ingress ' + name
15+
else:
16+
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses" % name
17+
message = 'get Ingresses'
18+
19+
response = self.http_get(url, params=self.query_params(**kwargs))
20+
if self.unhealthy(response.status_code):
21+
raise KubeHTTPException(response, message)
22+
23+
return response
24+
25+
def create(self, ingress, namespace, hostname):
26+
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses" % namespace
27+
28+
data = {
29+
"kind": "Ingress",
30+
"apiVersion": "extensions/v1beta1",
31+
"metadata": {
32+
"name": ingress
33+
},
34+
"spec": {
35+
"rules": [
36+
{"host": ingress + "." + hostname,
37+
"http": {
38+
"paths": [
39+
{"path": "/",
40+
"backend": {
41+
"serviceName": ingress,
42+
"servicePort": 80
43+
}}
44+
]
45+
}
46+
}
47+
]
48+
}
49+
}
50+
response = self.http_post(url, json=data)
51+
52+
if not response.status_code == 201:
53+
raise KubeHTTPException(response, "create Ingress {}".format(namespace))
54+
55+
return response
56+
57+
def delete(self, namespace, ingress):
58+
url = self.api("/namespaces/{}/ingresses/{}", namespace, ingress)
59+
response = self.http_delete(url)
60+
if self.unhealthy(response.status_code):
61+
raise KubeHTTPException(response, 'delete Ingress "{}"', namespace)
62+
63+
return response

0 commit comments

Comments
 (0)