Skip to content

Commit

Permalink
feat(ingress): Feature work for experimental native ingress
Browse files Browse the repository at this point in the history
Adding ingress support to controller
Adding changes to chart to support feature
Adding ingress rules for controller API, and SSH on TCP 2222
Adding ingress support in general

Requires deis/workflow#732
Requires deis/builder#495
Requires deis/router#316

Technically a non breaking change, as the user must opt-in to the feature
  • Loading branch information
krisnova authored and mboersma committed Mar 30, 2017
1 parent 70e99f4 commit 726a587
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 1 deletion.
5 changes: 5 additions & 0 deletions charts/controller/templates/controller-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ spec:
# NOTE(bacongobbler): use deis/registry_proxy to work around Docker --insecure-registry requirements
- name: "DEIS_REGISTRY_SERVICE_HOST"
value: "127.0.0.1"
# Environmental variable value for $EXPERIMENTAL_NATIVE_INGRESS
- name: "EXPERIMENTAL_NATIVE_INGRESS"
value: "{{ .Values.global.experimental_native_ingress }}"
- name: "EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME"
value: "{{ .Values.platform_domain }}"
- name: "K8S_API_VERIFY_TLS"
value: "{{ .Values.k8s_api_verify_tls }}"
- name: "DEIS_REGISTRY_SERVICE_PORT"
Expand Down
21 changes: 21 additions & 0 deletions charts/controller/templates/controller-ingress-rule-http-80.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{ if .Values.global.experimental_native_ingress }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: "deis"
name: "controller-api-server-ingress-http"
labels:
app: "controller"
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
rules:
- host: deis.{{ .Values.platform_domain }}
http:
paths:
- path: /
backend:
serviceName: deis-controller
servicePort: 80
{{- end }}
10 changes: 10 additions & 0 deletions charts/controller/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ deploy_hook_urls: ""
registration_mode: "admin_only"
# Option to disable ssl verification to connect to k8s api server
k8s_api_verify_tls: "true"
# The public resolvable hostname to build your cluster with.
#
# This will be the hostname that is used to build endpoints such as "deis.$HOSTNAME"
platform_domain: ""

global:
# Set the storage backend
Expand Down Expand Up @@ -45,3 +49,9 @@ global:
host_port: 5555
# Prefix for the imagepull secret created when using private registry
secret_prefix: "private-registry"
# Experimental feature to toggle using kubernetes ingress instead of the Deis router.
#
# Valid values are:
# - true: The deis controller will now create Kubernetes ingress rules for each app, and ingress rules will automatically be created for the controller itself.
# - false: The default mode, and the default behavior of Deis workflow.
experimental_native_ingress: false
14 changes: 13 additions & 1 deletion rootfs/api/models/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def create(self, *args, **kwargs): # noqa

# create required minimum resources in k8s for the application
namespace = self.id
ingress = self.id
service = self.id
quota_name = '{}-quota'.format(self.id)
try:
Expand All @@ -200,7 +201,6 @@ def create(self, *args, **kwargs): # noqa
self._scheduler.ns.get(namespace)
except KubeException:
self._scheduler.ns.create(namespace)

if settings.KUBERNETES_NAMESPACE_DEFAULT_QUOTA_SPEC != '':
quota_spec = json.loads(settings.KUBERNETES_NAMESPACE_DEFAULT_QUOTA_SPEC)
self.log('creating Quota {} for namespace {}'.format(quota_name, namespace),
Expand All @@ -224,6 +224,18 @@ def create(self, *args, **kwargs): # noqa

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

try:
# In order to create an ingress, we must first have a namespace.
if settings.EXPERIMENTAL_NATIVE_INGRESS:
try:
self._scheduler.ingress.get(ingress)
except KubeException:
self.log("creating Ingress {}".format(namespace), level=logging.INFO)
self._scheduler.ingress.create(ingress,
namespace,
settings.EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME)
except KubeException as e:
raise ServiceUnavailable('Could not create Ingress in Kubernetes') from e
try:
self.appsettings_set.latest()
except AppSettings.DoesNotExist:
Expand Down
4 changes: 4 additions & 0 deletions rootfs/api/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@
SECRET_KEY = os.environ.get('DEIS_SECRET_KEY', random_secret)
BUILDER_KEY = os.environ.get('DEIS_BUILDER_KEY', random_secret)

# experimental native ingress
EXPERIMENTAL_NATIVE_INGRESS = bool(os.environ.get('EXPERIMENTAL_NATIVE_INGRESS', 0))
EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME = os.environ.get('EXPERIMENTAL_NATIVE_INGRESS_HOSTNAME', '')

# k8s image policies
SLUGRUNNER_IMAGE = os.environ.get('SLUGRUNNER_IMAGE_NAME', 'quay.io/deisci/slugrunner:canary') # noqa
IMAGE_PULL_POLICY = os.environ.get('IMAGE_PULL_POLICY', "IfNotPresent") # noqa
Expand Down
66 changes: 66 additions & 0 deletions rootfs/scheduler/resources/ingress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from scheduler.exceptions import KubeHTTPException
from scheduler.resources import Resource


class Ingress(Resource):
short_name = 'ingress'

def get(self, name=None, **kwargs):
"""
Fetch a single Ingress or a list of Ingresses
"""
if name is not None:
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses/%s" % (name, name)
message = 'get Ingress ' + name
else:
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses" % name
message = 'get Ingresses'

response = self.http_get(url, params=self.query_params(**kwargs))
if self.unhealthy(response.status_code):
raise KubeHTTPException(response, message)

return response

def create(self, ingress, namespace, hostname):
url = "/apis/extensions/v1beta1/namespaces/%s/ingresses" % namespace

if hostname == "":
raise KubeHTTPException("empty hostname value")

data = {
"kind": "Ingress",
"apiVersion": "extensions/v1beta1",
"metadata": {
"name": ingress
},
"spec": {
"rules": [
{"host": ingress + "." + hostname,
"http": {
"paths": [
{"path": "/",
"backend": {
"serviceName": ingress,
"servicePort": 80
}}
]
}
}
]
}
}
response = self.http_post(url, json=data)

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

return response

def delete(self, namespace, ingress):
url = self.api("/namespaces/{}/ingresses/{}", namespace, ingress)
response = self.http_delete(url)
if self.unhealthy(response.status_code):
raise KubeHTTPException(response, 'delete Ingress "{}"', namespace)

return response

0 comments on commit 726a587

Please sign in to comment.