diff --git a/Makefile b/Makefile index bb6b19a1..8f5bdc72 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/chart/crds/dashboard.openstack.org_horizons.yaml b/chart/crds/dashboard.openstack.org_horizons.yaml new file mode 100644 index 00000000..600b0fe3 --- /dev/null +++ b/chart/crds/dashboard.openstack.org_horizons.yaml @@ -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 diff --git a/chart/templates/clusterrole.yaml b/chart/templates/clusterrole.yaml index 23314dad..0e70bb93 100644 --- a/chart/templates/clusterrole.yaml +++ b/chart/templates/clusterrole.yaml @@ -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: diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index 1bd8bc2e..adc30a43 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -29,6 +29,8 @@ spec: args: - run - -m + - openstack_operator.horizon + - -m - openstack_operator.mcrouter - -m - openstack_operator.memcached diff --git a/config/samples/dashboard_v1alpha1_horizon.yaml b/config/samples/dashboard_v1alpha1_horizon.yaml new file mode 100644 index 00000000..33a12120 --- /dev/null +++ b/config/samples/dashboard_v1alpha1_horizon.yaml @@ -0,0 +1,9 @@ +apiVersion: dashboard.openstack.org/v1alpha1 +kind: Horizon +metadata: + name: sample + labels: + prometheus: helm +spec: + ingress: + host: "horizon.vexxhost.com" \ No newline at end of file diff --git a/images/horizon/Dockerfile b/images/horizon/Dockerfile new file mode 100644 index 00000000..bf44bc3e --- /dev/null +++ b/images/horizon/Dockerfile @@ -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" diff --git a/images/horizon/bindep.txt b/images/horizon/bindep.txt new file mode 100644 index 00000000..118209ad --- /dev/null +++ b/images/horizon/bindep.txt @@ -0,0 +1,3 @@ +gcc [compile] +libc-dev [compile] +mime-support diff --git a/images/horizon/logo-splash.svg b/images/horizon/logo-splash.svg new file mode 100644 index 00000000..dbb47724 --- /dev/null +++ b/images/horizon/logo-splash.svg @@ -0,0 +1,31 @@ + + Stack + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/horizon/logo.svg b/images/horizon/logo.svg new file mode 100644 index 00000000..76872699 --- /dev/null +++ b/images/horizon/logo.svg @@ -0,0 +1,31 @@ + + Horizontal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/horizon/manage.py b/images/horizon/manage.py new file mode 100644 index 00000000..7894ec89 --- /dev/null +++ b/images/horizon/manage.py @@ -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) diff --git a/openstack_operator/horizon.py b/openstack_operator/horizon.py new file mode 100644 index 00000000..1c944c4d --- /dev/null +++ b/openstack_operator/horizon.py @@ -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) diff --git a/openstack_operator/objects.py b/openstack_operator/objects.py index 4bcf3366..5f5fca73 100644 --- a/openstack_operator/objects.py +++ b/openstack_operator/objects.py @@ -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, diff --git a/openstack_operator/templates/horizon/configmap.yml.j2 b/openstack_operator/templates/horizon/configmap.yml.j2 new file mode 100644 index 00000000..1fc6e5da --- /dev/null +++ b/openstack_operator/templates/horizon/configmap.yml.j2 @@ -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 diff --git a/openstack_operator/templates/horizon/deployment.yml.j2 b/openstack_operator/templates/horizon/deployment.yml.j2 new file mode 100644 index 00000000..d7ce451b --- /dev/null +++ b/openstack_operator/templates/horizon/deployment.yml.j2 @@ -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 %} diff --git a/openstack_operator/templates/horizon/ingress.yml.j2 b/openstack_operator/templates/horizon/ingress.yml.j2 new file mode 100644 index 00000000..ebb4dd35 --- /dev/null +++ b/openstack_operator/templates/horizon/ingress.yml.j2 @@ -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 diff --git a/openstack_operator/templates/horizon/memcached.yml.j2 b/openstack_operator/templates/horizon/memcached.yml.j2 new file mode 100644 index 00000000..3cbebd94 --- /dev/null +++ b/openstack_operator/templates/horizon/memcached.yml.j2 @@ -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 diff --git a/openstack_operator/templates/horizon/secret-config.yml.j2 b/openstack_operator/templates/horizon/secret-config.yml.j2 new file mode 100644 index 00000000..f947f7a0 --- /dev/null +++ b/openstack_operator/templates/horizon/secret-config.yml.j2 @@ -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 }} diff --git a/openstack_operator/templates/horizon/secret-secretkey.yml.j2 b/openstack_operator/templates/horizon/secret-secretkey.yml.j2 new file mode 100644 index 00000000..8453f65e --- /dev/null +++ b/openstack_operator/templates/horizon/secret-secretkey.yml.j2 @@ -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 }} diff --git a/openstack_operator/templates/horizon/service.yml.j2 b/openstack_operator/templates/horizon/service.yml.j2 new file mode 100644 index 00000000..782f1b21 --- /dev/null +++ b/openstack_operator/templates/horizon/service.yml.j2 @@ -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) }} diff --git a/openstack_operator/tests/unit/base.py b/openstack_operator/tests/unit/base.py index 28ebff31..739a0a55 100644 --- a/openstack_operator/tests/unit/base.py +++ b/openstack_operator/tests/unit/base.py @@ -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) diff --git a/openstack_operator/tests/unit/test_horizon.py b/openstack_operator/tests/unit/test_horizon.py new file mode 100644 index 00000000..e3615c5a --- /dev/null +++ b/openstack_operator/tests/unit/test_horizon.py @@ -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' diff --git a/openstack_operator/utils.py b/openstack_operator/utils.py index e7ae264f..3139346c 100644 --- a/openstack_operator/utils.py +++ b/openstack_operator/utils.py @@ -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())) diff --git a/zuul.d/functional-jobs.yaml b/zuul.d/functional-jobs.yaml index a3c1ca33..727a4618 100644 --- a/zuul.d/functional-jobs.yaml +++ b/zuul.d/functional-jobs.yaml @@ -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 diff --git a/zuul.d/horizon-jobs.yaml b/zuul.d/horizon-jobs.yaml new file mode 100644 index 00000000..2da2675f --- /dev/null +++ b/zuul.d/horizon-jobs.yaml @@ -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