Add Horizon Dockerfile and operator

Change-Id: I9bab4c3a66a33a4a8289509eb8c93232d56dfe6a
This commit is contained in:
okozachenko 2020-04-21 08:38:16 -07:00 committed by Mohammed Naser
parent 9451d3aaee
commit be4e5eee89
24 changed files with 625 additions and 13 deletions

View File

@ -1,4 +1,5 @@
images:
docker build images/horizon -t vexxhost/horizon:latest
docker build images/ceilometer --target ceilometer-agent-notification -t vexxhost/ceilometer-agent-notification:latest
docker build images/mcrouter -t vexxhost/mcrouter:latest
docker build images/mcrouter-exporter -t vexxhost/mcrouter-exporter:latest

View File

@ -0,0 +1,19 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: horizons.dashboard.openstack.org
spec:
group: dashboard.openstack.org
names:
kind: Horizon
listKind: HorizonList
plural: horizons
singular: horizon
scope: Namespaced
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true

View File

@ -129,6 +129,26 @@ rules:
- get
- patch
- update
- apiGroups:
- dashboard.openstack.org
resources:
- horizons
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- dashboard.openstack.org
resources:
- horizons/status
verbs:
- get
- patch
- update
- apiGroups:
- infrastructure.vexxhost.cloud
resources:

View File

@ -29,6 +29,8 @@ spec:
args:
- run
- -m
- openstack_operator.horizon
- -m
- openstack_operator.mcrouter
- -m
- openstack_operator.memcached

View File

@ -0,0 +1,9 @@
apiVersion: dashboard.openstack.org/v1alpha1
kind: Horizon
metadata:
name: sample
labels:
prometheus: helm
spec:
ingress:
host: "horizon.vexxhost.com"

48
images/horizon/Dockerfile Normal file
View File

@ -0,0 +1,48 @@
# Copyright (c) 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM docker.io/opendevorg/python-builder as builder
COPY bindep.txt /tmp/src/bindep.txt
RUN assemble horizon==18.3.1 \
python-memcached \
heat-dashboard==2.0.0 \
designate-dashboard==9.0.0 \
neutron-vpnaas-dashboard==1.6.0 \
octavia-dashboard==4.0.0 \
sahara-dashboard==11.0.0 \
magnum-ui==6.0.0
FROM docker.io/opendevorg/uwsgi-base
COPY --from=builder /output/ /output
RUN /output/install-from-bindep
WORKDIR /usr/local/lib/python3.7/site-packages/openstack_dashboard
RUN ln -s /etc/horizon/local_settings.py local/local_settings.py && \
cp ../designatedashboard/enabled/*.py enabled/ && \
cp ../heat_dashboard/enabled/*.py enabled/ && \
cp ../magnum_ui/enabled/*.py enabled/ && \
cp ../neutron_vpnaas_dashboard/enabled/*.py enabled/ && \
cp ../octavia_dashboard/enabled/*.py enabled/ && \
cp ../sahara_dashboard/enabled/*.py enabled/
COPY *.svg ../static/dashboard/img/
COPY manage.py .
RUN python manage.py collectstatic --no-input && \
python manage.py compress --force && \
chown 1001 -R local/ ../static
EXPOSE 8000
ENV UWSGI_HTTP_SOCKET=:8000 \
UWSGI_WSGI_FILE=/usr/local/lib/python3.7/site-packages/openstack_dashboard/wsgi/django.wsgi \
UWSGI_CHECK_STATIC=/usr/local/lib/python3.7/site-packages/static/ \
UWSGI_STATIC_MAP="/static=/usr/local/lib/python3.7/site-packages/static/" \
UWSGI_MIME_FILE="/etc/mime.types"

View File

@ -0,0 +1,3 @@
gcc [compile]
libc-dev [compile]
mime-support

View File

@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98.43 118.4">
<title>Stack</title>
<g>
<g>
<g>
<path d="M54.79,76.45h1.58L46,62l-1,.93Z" style="fill: #2a1659"/>
<path d="M21.85,28.46H20.09l18.37,25.4c.27-.39.55-.77.83-1.16Z" style="fill: #2a1659"/>
<path d="M25.32,28.46H23.57l16.61,23L41,50.32Z" style="fill: #2a1659"/>
<path d="M58.23,76.45h1.59L48,60l-1,.91Z" style="fill: #2a1659"/>
<path d="M62.42,76.45h9.76L55.7,53.51c-2.11,1.65-4.16,3.35-6.13,5.11Z" style="fill: #2a1659"/>
<path d="M37.71,28.46H27.84L42.35,48.59c1.68-2.16,3.45-4.26,5.28-6.31Z" style="fill: #2a1659"/>
</g>
<path d="M95.13,33.36a47.16,47.16,0,0,1,3.3,16.91A48.89,48.89,0,0,1,86.49,82.59,48.41,48.41,0,0,1,37.76,97.86a48.36,48.36,0,0,1-16.31-7.51A50.18,50.18,0,0,1,.78,43.09a50.13,50.13,0,0,1,16.49-31,48.71,48.71,0,0,1,15.2-9.13A47.38,47.38,0,0,1,49.84,0,46.31,46.31,0,0,1,81.36,12.87,47.3,47.3,0,0,0,49.86,2.46,45.08,45.08,0,0,0,33.67,6a46.59,46.59,0,0,0-13.78,9,47.76,47.76,0,0,0-9.76,13.11,46.27,46.27,0,0,0,2.93,46.63,45.72,45.72,0,0,0,11.22,11.7A44.9,44.9,0,0,0,39,93.29a45.78,45.78,0,0,0,31.92-3.08,47.32,47.32,0,0,0,22.3-23.67,49.09,49.09,0,0,0,3.88-16.31A49.59,49.59,0,0,0,95.13,33.36Z" style="fill: #2a1659"/>
<g>
<path d="M30.3,75.44c.75-1.34,1.45-2.57,2.11-3.72C31.68,73,31,74.19,30.3,75.44Z" style="fill: none"/>
<path d="M95.09,12.49l0-.05h-.13S95,12.44,95.09,12.49Z" style="fill: #9eb3e5"/>
<path d="M36.1,66.39a97.62,97.62,0,0,0-5.8,9c.67-1.25,1.38-2.49,2.11-3.72h0c1.56-2.64,3.26-5.23,5.07-7.78h0c15.24-21.36,37.74-38.09,59.39-46l-1.78-5.45c-.07-.05-.14-.08-.17-.05-30.49,11-58.66,39.46-68.5,71.15.48-1.16,1-2.31,1.53-3.46C42.06,57,69.19,38.07,94.45,29.81l-.75-5.18C71.48,31.61,50.08,47,36.1,66.39Z" style="fill: #6536ff"/>
</g>
</g>
<g>
<path d="M10.82,107.44,6.29,118.21H4.55L0,107.44H1.61l3.8,9.3,3.8-9.3Z" style="fill: #2a1659"/>
<path d="M21.25,107.44v1.2h-6v2.3h-1.4v-3.5Zm-.11,5.87H13.87v-1.2h7.27ZM15.27,117h6v1.2H13.87v-3.74h1.4Z" style="fill: #2a1659"/>
<path d="M34.87,107.39l-8.79,10.82H24.3l8.79-10.82Zm-7.07,4.29-3.5-4.29h1.81l2.59,3.2Zm7.08,6.53H33.11L30.49,115l.89-1.1Z" style="fill: #2a1659"/>
<path d="M48.5,107.39l-8.8,10.82H37.93l8.79-10.82Zm-7.07,4.29-3.5-4.29h1.81l2.59,3.2Zm7.08,6.53H46.74L44.12,115l.89-1.1Z" style="fill: #2a1659"/>
<path d="M61,112.07v6.14H59.55v-4.94h-8v-5.83H53v4.63Zm-9.4,6.14v-3.77H53v3.77Zm8-7.3v-3.47H61v3.47Z" style="fill: #6536ff"/>
<path d="M75.21,112.82a5.61,5.61,0,1,1-5.61-5.57A5.38,5.38,0,0,1,75.21,112.82Zm-1.45,0a4.16,4.16,0,1,0-4.16,4.37A4.08,4.08,0,0,0,73.76,112.82Z" style="fill: #6536ff"/>
<path d="M86.75,115.23c0,1.56-1.12,3.17-4.16,3.17a5.74,5.74,0,0,1-4.33-1.71l.84-1a4.75,4.75,0,0,0,3.54,1.55c2,0,2.65-1,2.65-1.87,0-2.78-6.68-1.22-6.68-5.06,0-1.77,1.66-3,3.89-3a5.42,5.42,0,0,1,4,1.51l-.84,1a4.39,4.39,0,0,0-3.27-1.29c-1.33,0-2.31.7-2.31,1.71C80.07,112.6,86.75,111.21,86.75,115.23Z" style="fill: #6536ff"/>
<path d="M89.78,108.64v-1.2h8.55v1.2Zm3.56,9.57v-8.4h1.41v8.4Z" style="fill: #6536ff"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

31
images/horizon/logo.svg Normal file
View File

@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560.46 99.32">
<title>Horizontal</title>
<g>
<g>
<path d="M172.92,28.88l-20.07,47.7h-7.72L125,28.88h7.11L149,70.06,165.8,28.88Z" style="fill: #2a1659"/>
<path d="M219.12,28.88v5.31H192.61V44.38H186.4V28.88Zm-.52,26H186.4V49.55h32.2Zm-26,16.4h26.51v5.32H186.4V60h6.21Z" style="fill: #2a1659"/>
<path d="M279.41,28.65,240.47,76.58H232.6l38.94-47.93Zm-31.31,19-15.5-19h8l11.46,14.16Zm31.38,28.91h-7.86L260,62.35l4-4.87Z" style="fill: #2a1659"/>
<path d="M339.77,28.65,300.82,76.58H293L331.9,28.65Zm-31.31,19-15.5-19h8l11.46,14.16Zm31.38,28.91H332L320.37,62.35l4-4.87Z" style="fill: #2a1659"/>
<path d="M395,49.4V76.58h-6.22V54.71H353.32V28.88h6.22V49.4ZM353.32,76.58V59.88h6.22v16.7Zm35.42-32.35V28.88H395V44.23Z" style="fill: #6536ff"/>
<path d="M458.09,52.69c0,14.08-10.11,24.64-24.86,24.64s-24.79-10.56-24.79-24.64,10-24.64,24.79-24.64S458.09,38.61,458.09,52.69Zm-6.44,0c0-11.16-7.27-19.4-18.42-19.4s-18.35,8.24-18.35,19.4,7.11,19.4,18.35,19.4S451.65,63.77,451.65,52.69Z" style="fill: #6536ff"/>
<path d="M509.16,63.4c0,6.89-4.94,14-18.42,14-8.61,0-15.05-3.14-19.17-7.56l3.74-4.57A20.94,20.94,0,0,0,491,72.16c8.91,0,11.75-4.57,11.75-8.31,0-12.28-29.58-5.39-29.58-22.39,0-7.87,7.34-13.33,17.23-13.33,7.56,0,13.48,2.47,17.67,6.66l-3.74,4.35c-3.75-4-9-5.7-14.46-5.7-5.91,0-10.26,3.07-10.26,7.57C479.58,51.72,509.16,45.58,509.16,63.4Z" style="fill: #6536ff"/>
<path d="M522.57,34.19V28.88h37.89v5.31Zm15.8,42.39V39.36h6.21V76.58Z" style="fill: #6536ff"/>
</g>
<g>
<g>
<path d="M54.39,76.45H56L45.55,62l-.95.93Z" style="fill: #2a1659"/>
<path d="M21.45,28.46H19.69l18.37,25.4c.27-.39.55-.77.83-1.16Z" style="fill: #2a1659"/>
<path d="M24.92,28.46H23.17l16.61,23,.85-1.13Z" style="fill: #2a1659"/>
<path d="M57.83,76.45h1.59L47.61,60l-1,.91Z" style="fill: #2a1659"/>
<path d="M62,76.45h9.75L55.3,53.51c-2.11,1.65-4.16,3.35-6.13,5.11Z" style="fill: #2a1659"/>
<path d="M37.31,28.46H27.44L42,48.59c1.68-2.16,3.45-4.26,5.28-6.31Z" style="fill: #2a1659"/>
</g>
<path d="M94.73,33.36A47.16,47.16,0,0,1,98,50.27,48.89,48.89,0,0,1,86.09,82.59,48.41,48.41,0,0,1,37.36,97.86a48.36,48.36,0,0,1-16.31-7.51A50.18,50.18,0,0,1,.38,43.09a50.13,50.13,0,0,1,16.49-31,48.71,48.71,0,0,1,15.2-9.13A47.38,47.38,0,0,1,49.44,0,46.31,46.31,0,0,1,81,12.87,47.3,47.3,0,0,0,49.46,2.46,45.08,45.08,0,0,0,33.27,6a46.59,46.59,0,0,0-13.78,9A47.76,47.76,0,0,0,9.73,28.05a46.27,46.27,0,0,0,2.93,46.63,45.72,45.72,0,0,0,11.22,11.7,44.9,44.9,0,0,0,14.67,6.91,45.78,45.78,0,0,0,31.92-3.08A47.25,47.25,0,0,0,92.76,66.54a48.86,48.86,0,0,0,3.89-16.31A49.59,49.59,0,0,0,94.73,33.36Z" style="fill: #2a1659"/>
<g>
<path d="M29.9,75.44c.75-1.34,1.45-2.57,2.11-3.72C31.28,73,30.57,74.19,29.9,75.44Z" style="fill: none"/>
<path d="M94.69,12.49l0-.05h-.13S94.61,12.44,94.69,12.49Z" style="fill: #9eb3e5"/>
<path d="M35.7,66.39a95.58,95.58,0,0,0-5.8,9c.67-1.25,1.38-2.49,2.11-3.72h0c1.56-2.64,3.26-5.23,5.07-7.78h0c15.24-21.36,37.74-38.09,59.39-46l-1.78-5.45c-.08-.05-.14-.08-.17-.05C64,23.48,35.86,51.9,26,83.59c.48-1.16,1-2.31,1.53-3.46C41.66,57,68.79,38.07,94.05,29.81l-.75-5.18C71.08,31.61,49.68,47,35.7,66.39Z" style="fill: #6536ff"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

9
images/horizon/manage.py Normal file
View File

@ -0,0 +1,9 @@
import os
import sys
from django.core.management import execute_from_command_line
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
"openstack_dashboard.settings")
execute_from_command_line(sys.argv)

View File

@ -0,0 +1,81 @@
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""horizon Operator
This module maintains the operator for Mcrouter, it takes care of creating
the appropriate deployments, Mcrouter, pod monitors and Prometheus rules.
"""
import kopf
from openstack_operator import utils
@kopf.on.create('dashboard.openstack.org', 'v1alpha1', 'horizons')
def create_secret(name, **_):
"""Create a new horizon secret"""
utils.create_or_update('horizon/secret-secretkey.yml.j2',
name=name,
secret=utils.generate_password())
@kopf.on.resume('dashboard.openstack.org', 'v1alpha1', 'horizons')
@kopf.on.create('dashboard.openstack.org', 'v1alpha1', 'horizons')
def create_or_resume(namespace, name, spec, **_):
"""Create and re-sync a horizon instance
This function is called when a new resource is created but also when we
start the service up for the first time.
"""
# Grab the secretkey secret
secret_key = utils.get_secret(namespace, "horizon-%s-secretkey" % name)
utils.create_or_update('horizon/secret-config.yml.j2',
name=name,
secret=secret_key['secret_key'])
conn = utils.get_openstack_connection()
auth_url = conn.config.auth["auth_url"]
config = utils.create_or_update('horizon/configmap.yml.j2',
name=name, spec=spec, auth_url=auth_url)
config_hash = utils.generate_hash(config.obj['data'])
utils.create_or_update('horizon/deployment.yml.j2',
config_hash=config_hash, name=name, spec=spec)
utils.create_or_update('horizon/service.yml.j2',
name=name, spec=spec)
utils.create_or_update('horizon/memcached.yml.j2',
name=name, spec=spec)
if "ingress" in spec:
utils.create_or_update('horizon/ingress.yml.j2',
name=name, spec=spec)
@kopf.on.update('dashboard.openstack.org', 'v1alpha1', 'horizons')
def update(name, spec, **_):
"""Update a horizon
This function updates the deployment for horizon if there are any
changes that happen within it.
"""
conn = utils.get_openstack_connection()
auth_url = conn.config.auth["auth_url"]
config = utils.create_or_update('horizon/configmap.yml.j2',
name=name, spec=spec, auth_url=auth_url)
config_hash = utils.generate_hash(config.obj['data'])
utils.create_or_update('horizon/deployment.yml.j2',
config_hash=config_hash, name=name, spec=spec)
if hasattr(spec, "ingress"):
utils.create_or_update('horizon/ingress.yml.j2',
name=name, spec=spec)

View File

@ -24,8 +24,10 @@ from combinations of apiVersion and kind to the exact model.
from pykube.objects import ConfigMap
from pykube.objects import Deployment
from pykube.objects import Ingress
from pykube.objects import NamespacedAPIObject
from pykube.objects import Pod
from pykube.objects import Secret
from pykube.objects import Service
from pykube.objects import StatefulSet
@ -38,6 +40,14 @@ class Mcrouter(NamespacedAPIObject):
kind = "Mcrouter"
class Memcached(NamespacedAPIObject):
"""Memcached Kubernetes object"""
version = "infrastructure.vexxhost.cloud/v1alpha1"
endpoint = "memcacheds"
kind = "Memcached"
class PodMonitor(NamespacedAPIObject):
"""PodMonitor Kubernetes object"""
@ -58,14 +68,19 @@ MAPPING = {
"v1": {
"ConfigMap": ConfigMap,
"Pod": Pod,
"Secret": Secret,
"Service": Service,
},
"apps/v1": {
"Deployment": Deployment,
"StatefulSet": StatefulSet,
},
"extensions/v1beta1": {
"Ingress": Ingress
},
"infrastructure.vexxhost.cloud/v1alpha1": {
"Mcrouter": Mcrouter,
"Memcached": Memcached
},
"monitoring.coreos.com/v1": {
"PodMonitor": PodMonitor,

View File

@ -0,0 +1,39 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: ConfigMap
metadata:
name: horizon-{{ name }}
namespace: default
data:
local_settings.py: |
import os
from openstack_dashboard.settings import HORIZON_CONFIG
ALLOWED_HOSTS = ['*']
SECRET_KEY = os.getenv('SECRET_KEY')
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'mcrouter-memcached-horizon-{{ name }}:11211',
},
}
OPENSTACK_KEYSTONE_URL = '{{ auth_url }}'
OPENSTACK_NEUTRON_NETWORK['enable_ha_router'] = True

View File

@ -0,0 +1,79 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apps/v1
kind: Deployment
metadata:
name: horizon-{{ name }}
labels:
{{ labels("horizon", name) | indent(4) }}
annotations:
checksum/config: "{{ config_hash }}"
spec:
replicas: 3
selector:
matchLabels:
{{ labels("horizon", name) | indent(6) }}
template:
metadata:
labels:
{{ labels("horizon", name) | indent(8) }}
spec:
containers:
- name: horizon
image: vexxhost/horizon:latest
imagePullPolicy: Always
env:
- name: SECRET_KEY
valueFrom:
secretKeyRef:
key: secret_key
name: horizon-{{ name }}
ports:
- name: horizon
containerPort: 8000
livenessProbe:
tcpSocket:
port: horizon
readinessProbe:
tcpSocket:
port: horizon
resources:
limits:
cpu: 1000m
ephemeral-storage: 500M
memory: 256M
requests:
cpu: 500m
ephemeral-storage: 500M
memory: 128M
securityContext:
runAsUser: 1001
volumeMounts:
- mountPath: /etc/horizon
name: config
volumes:
- configMap:
defaultMode: 420
name: horizon-{{ name }}
name: config
{% if 'nodeSelector' in spec %}
nodeSelector:
{{ spec.nodeSelector | to_yaml | indent(8) }}
{% endif %}
{% if 'tolerations' in spec %}
tolerations:
{{ spec.tolerations | to_yaml | indent(8) }}
{% endif %}

View File

@ -0,0 +1,28 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: horizon-{{ name }}
spec:
rules:
- host: {{ spec.ingress.host }}
http:
paths:
- path: /
backend:
serviceName: horizon-{{ name }}
servicePort: 80

View File

@ -0,0 +1,23 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: infrastructure.vexxhost.cloud/v1alpha1
kind: Memcached
metadata:
name: horizon-{{ name }}
labels:
{{ labels("horizon", name) | indent(4) }}
spec:
megabytes: 128

View File

@ -0,0 +1,21 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: Secret
metadata:
name: horizon-{{ name }}
stringData:
secret_key: {{ secret }}

View File

@ -0,0 +1,21 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: Secret
metadata:
name: horizon-{{ name }}-secretkey
stringData:
secret_key: {{ secret }}

View File

@ -0,0 +1,28 @@
---
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: Service
metadata:
name: horizon-{{ name }}
spec:
type: ClusterIP
ports:
- name: horizon
port: 80
protocol: TCP
targetPort: horizon
selector:
{{ labels("horizon", name) | indent(4) }}

View File

@ -34,7 +34,6 @@ class KubernetesObjectTestCase(testtools.TestCase):
SAMPLES_PATH = 'config/samples'
SAMPLE_FILE = ''
TEMPLATE_FILE = ''
@classmethod
def setUpClass(cls):
sample_path = "%s/%s" % (cls.SAMPLES_PATH, cls.SAMPLE_FILE)

View File

@ -0,0 +1,27 @@
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for Horizon Operator
This module contains all the tests for the Horizon operator.
"""
from openstack_operator.tests.unit import base
class HorizonDeploymentTestCase(base.DeploymentTestCase):
"""Basic tests for the Deployment."""
SAMPLE_FILE = 'dashboard_v1alpha1_horizon.yaml'
TEMPLATE_FILE = 'horizon/deployment.yml.j2'

View File

@ -17,17 +17,19 @@
The module contains a few useful utilities which we refactor out in order
to be able to use them across all different operators.
"""
import base64
import copy
import operator
import os
import secrets
import string
import jinja2
import openstack
import kopf
from pbr import version
import pykube
import yaml
import openstack
from openstack_operator import objects
@ -41,6 +43,11 @@ def to_yaml(value):
return yaml.safe_dump(value)
def to_dict(value):
"""Return a dictionary from a YAML string"""
return yaml.safe_load(value)
def labels(app, instance, component=None):
"""Return standard labels for the operator."""
metadata = {
@ -82,6 +89,8 @@ def create_or_update(template, **kwargs):
raise
resource.create()
return resource
def ensure_absent(template, **kwargs):
"""Ensure a Kubernetes resource bound to a template is deleted
@ -156,3 +165,37 @@ def get_openstack_connection():
"""Get an instance of OpenStack SDK."""
return openstack.connect(cloud="envvars", app_name='openstack-operator',
app_version=VERSION)
def generate_password(length=20):
"""Generate a random password."""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for i in range(length))
def get_secret(namespace, name):
"""Retrieve a secret from Kubernetes.
This function retrieves a Secret from Kubernetes, decodes it and passes
the value of the data
"""
api = pykube.HTTPClient(pykube.KubeConfig.from_env())
secret = objects.Secret.objects(api).get(
namespace=namespace,
name=name
)
return {
k: base64.b64decode(v).decode('utf-8')
for k, v in secret.obj['data'].items()
}
def generate_hash(dictionary):
"""Generate a has from a dictionary, return None if dictionary is empty"""
if not dictionary:
return None
return hash(frozenset(dictionary.items()))

View File

@ -20,33 +20,37 @@
jobs:
- openstack-operator:functional:
dependencies:
- name: openstack-operator:images:build:ceilometer
- name: openstack-operator:images:build:mcrouter-exporter
soft: true
- name: openstack-operator:images:build:horizon
soft: true
- name: openstack-operator:images:build:rabbitmq
soft: true
- name: openstack-operator:images:build:ceilometer
soft: true
- name: openstack-operator:images:build:memcached-exporter
soft: true
- name: openstack-operator:images:build:memcached
soft: true
- name: openstack-operator:images:build:mcrouter
soft: true
- openstack-operator:images:build:openstack-operator
- name: openstack-operator:images:build:mcrouter-exporter
soft: true
- name: openstack-operator:images:build:memcached-exporter
soft: true
gate:
jobs:
- openstack-operator:functional:
dependencies:
- name: openstack-operator:images:upload:ceilometer
- name: openstack-operator:images:upload:mcrouter-exporter
soft: true
- name: openstack-operator:images:upload:horizon
soft: true
- name: openstack-operator:images:upload:rabbitmq
soft: true
- name: openstack-operator:images:upload:ceilometer
soft: true
- name: openstack-operator:images:upload:memcached-exporter
soft: true
- name: openstack-operator:images:upload:memcached
soft: true
- name: openstack-operator:images:upload:mcrouter
soft: true
- openstack-operator:images:upload:openstack-operator
- name: openstack-operator:images:upload:mcrouter-exporter
soft: true
- name: openstack-operator:images:upload:memcached-exporter
soft: true

31
zuul.d/horizon-jobs.yaml Normal file
View File

@ -0,0 +1,31 @@
- job:
name: openstack-operator:images:build:horizon
parent: vexxhost-build-docker-image
provides: openstack-operator:image:horizon
vars: &id001
docker_images:
- context: images/horizon
repository: vexxhost/horizon
files: &id002
- ^images/horizon/.*
- job:
name: openstack-operator:images:upload:horizon
parent: vexxhost-upload-docker-image
provides: openstack-operator:image:horizon
vars: *id001
files: *id002
- job:
name: openstack-operator:images:promote:horizon
parent: vexxhost-promote-docker-image
vars: *id001
files: *id002
- project:
check:
jobs:
- openstack-operator:images:build:horizon
gate:
jobs:
- openstack-operator:images:upload:horizon
promote:
jobs:
- openstack-operator:images:promote:horizon