From 0e1c296101ffc19fae4ec5851b786ad33cd00f41 Mon Sep 17 00:00:00 2001 From: Steve Wilkerson Date: Tue, 4 Jun 2019 08:29:10 -0500 Subject: [PATCH] Horizon helm tests This adds a helm test for Horizon, the helm test runs a selenium webdriver check to verify the dashboard is up Change-Id: I3616c05596b2bd94931c39fb774333bf65453d52 Signed-off-by: Steve Wilkerson --- horizon/templates/bin/_selenium-test.py.tpl | 93 +++++++++++++++++++ horizon/templates/configmap-bin.yaml | 2 + horizon/templates/pod-helm-tests.yaml | 70 ++++++++++++++ horizon/templates/secret-keystone.yaml | 28 ++++++ horizon/values.yaml | 34 +++++++ tools/deployment/component/horizon/horizon.sh | 35 +++++++ .../developer/common/100-horizon.sh | 2 + zuul.d/jobs-openstack-helm.yaml | 16 ++++ zuul.d/project.yaml | 1 + 9 files changed, 281 insertions(+) create mode 100644 horizon/templates/bin/_selenium-test.py.tpl create mode 100644 horizon/templates/pod-helm-tests.yaml create mode 100644 horizon/templates/secret-keystone.yaml create mode 100755 tools/deployment/component/horizon/horizon.sh diff --git a/horizon/templates/bin/_selenium-test.py.tpl b/horizon/templates/bin/_selenium-test.py.tpl new file mode 100644 index 0000000000..8a6739172d --- /dev/null +++ b/horizon/templates/bin/_selenium-test.py.tpl @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +{{/* +Copyright 2019 The Openstack-Helm Authors. + +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. +*/}} + +import os +import sys +import logging +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.chrome.options import Options + +# Create logger, console handler and formatter +logger = logging.getLogger('Horizon Selenium Tests') +logger.setLevel(logging.DEBUG) +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + +ch.setFormatter(formatter) +logger.addHandler(ch) + +# Get keystone admin user name +if "OS_USERNAME" in os.environ: + keystone_user = os.environ['OS_USERNAME'] + logger.info('Found Keystone username') +else: + logger.critical('Keystone username environment variable not set') + sys.exit(1) +if "OS_PASSWORD" in os.environ: + keystone_password = os.environ['OS_PASSWORD'] + logger.info('Found Keystone password') +else: + logger.critical('Keystone password environment variable not set') + sys.exit(1) +if "HORIZON_URI" in os.environ: + horizon_uri = os.environ['HORIZON_URI'] + logger.info('Found Horizon URI') +else: + logger.critical('Horizon URI environment variable not set') + sys.exit(1) +if "OS_USER_DOMAIN_NAME" in os.environ: + user_domain_name = os.environ['OS_USER_DOMAIN_NAME'] + logger.info('Found Keystone user domain') +else: + logger.critical('Keystone user domain environment variable not set') + sys.exit(1) + +# Add options to make chrome browser headless +options = Options() +options.add_argument('--headless') +options.add_argument('--no-sandbox') + +browser = webdriver.Chrome('/etc/selenium/chromedriver', chrome_options=options) +browser.get(horizon_uri) + +try: + browser.find_element_by_name('domain').send_keys(user_domain_name) + browser.find_element_by_name('username').send_keys(keystone_user) + browser.find_element_by_name('password').send_keys(keystone_password) + logger.info("Successfully reached Horizon dashboard") +except: + logger.error('Unable to reach Horizon') + browser.close() + sys.exit(1) + +try: + browser.find_element_by_id('loginBtn').click() + WebDriverWait(browser, 15).until( + EC.presence_of_element_located((By.ID, 'navbar-collapse')) + ) + logger.info("Successfully logged into Horizon") +except: + logger.error("Unable to login to Horizon") + browser.close() + sys.exit(1) + +browser.close() diff --git a/horizon/templates/configmap-bin.yaml b/horizon/templates/configmap-bin.yaml index eef146d9e9..88d4b61a10 100644 --- a/horizon/templates/configmap-bin.yaml +++ b/horizon/templates/configmap-bin.yaml @@ -38,4 +38,6 @@ data: {{ tuple "bin/_manage.py.tpl" . | include "helm-toolkit.utils.template" | indent 4 }} django.wsgi: | {{ tuple "bin/_django.wsgi.tpl" . | include "helm-toolkit.utils.template" | indent 4 }} + selenium-test.py: | +{{ tuple "bin/_selenium-test.py.tpl" . | include "helm-toolkit.utils.template" | indent 4 }} {{- end }} diff --git a/horizon/templates/pod-helm-tests.yaml b/horizon/templates/pod-helm-tests.yaml new file mode 100644 index 0000000000..be6bfcdd49 --- /dev/null +++ b/horizon/templates/pod-helm-tests.yaml @@ -0,0 +1,70 @@ +{{/* +Copyright 2019 The Openstack-Helm Authors. + +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. +*/}} + +{{- if .Values.manifests.pod_helm_test }} +{{- $envAll := . }} + +{{- $mounts_tests := .Values.pod.mounts.horizon_tests.horizon_tests }} +{{- $mounts_tests_init := .Values.pod.mounts.horizon_tests.init_container }} + +{{- $serviceAccountName := print $envAll.Release.Name "-test" }} +{{ tuple $envAll "tests" $serviceAccountName | include "helm-toolkit.snippets.kubernetes_pod_rbac_serviceaccount" }} +--- +apiVersion: v1 +kind: Pod +metadata: + name: {{ print $envAll.Release.Name "-test" }} + labels: +{{ tuple $envAll "horizon" "test" | include "helm-toolkit.snippets.kubernetes_metadata_labels" | indent 4 }} + annotations: + "helm.sh/hook": test-success + {{ tuple $envAll | include "helm-toolkit.snippets.release_uuid" }} +spec: + restartPolicy: Never + serviceAccountName: {{ $serviceAccountName }} + nodeSelector: + {{ .Values.labels.test.node_selector_key }}: {{ .Values.labels.test.node_selector_value }} + initContainers: +{{ tuple $envAll "tests" $mounts_tests_init | include "helm-toolkit.snippets.kubernetes_entrypoint_init_container" | indent 4 }} + containers: + - name: {{ .Release.Name }}-test +{{ tuple $envAll "test" | include "helm-toolkit.snippets.image" | indent 6 }} +{{ tuple $envAll $envAll.Values.pod.resources.jobs.tests | include "helm-toolkit.snippets.kubernetes_resources" | indent 6 }} + command: + - /tmp/selenium-test.py + env: +{{- with $env := dict "ksUserSecret" .Values.secrets.identity.admin }} +{{- include "helm-toolkit.snippets.keystone_openrc_env_vars" $env | indent 8 }} +{{- end }} + - name: HORIZON_URI + value: {{ tuple "dashboard" "public" "web" . | include "helm-toolkit.endpoints.keystone_endpoint_uri_lookup" }} + volumeMounts: + - name: pod-tmp + mountPath: /tmp + - name: horizon-bin + mountPath: /tmp/selenium-test.py + subPath: selenium-test.py + readOnly: true +{{ if $mounts_tests.volumeMounts }}{{ toYaml $mounts_tests.volumeMounts | indent 8 }}{{ end }} + volumes: + - name: pod-tmp + emptyDir: {} + - name: horizon-bin + configMap: + name: horizon-bin + defaultMode: 0555 +{{ if $mounts_tests.volumes }}{{ toYaml $mounts_tests.volumes | indent 4 }}{{ end }} +{{- end }} diff --git a/horizon/templates/secret-keystone.yaml b/horizon/templates/secret-keystone.yaml new file mode 100644 index 0000000000..cbb32fe43d --- /dev/null +++ b/horizon/templates/secret-keystone.yaml @@ -0,0 +1,28 @@ +{{/* +Copyright 2017 The Openstack-Helm Authors. + +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. +*/}} + +{{- if .Values.manifests.secret_keystone }} +{{- $envAll := . }} +{{- $secretName := index $envAll.Values.secrets.identity.admin }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} +type: Opaque +data: +{{- tuple "admin" "internal" $envAll | include "helm-toolkit.snippets.keystone_secret_openrc" | indent 2 -}} +{{- end }} diff --git a/horizon/values.yaml b/horizon/values.yaml index e91d1929bf..1ee832317b 100644 --- a/horizon/values.yaml +++ b/horizon/values.yaml @@ -23,6 +23,7 @@ images: horizon_db_sync: docker.io/openstackhelm/horizon:ocata-ubuntu_xenial db_drop: docker.io/openstackhelm/heat:ocata-ubuntu_xenial horizon: docker.io/openstackhelm/horizon:ocata-ubuntu_xenial + test: docker.io/openstackhelm/osh-selenium:latest-ubuntu_xenial dep_check: quay.io/stackanetes/kubernetes-entrypoint:v0.3.1 image_repo_sync: docker.io/docker:17.07.0 pull_policy: "IfNotPresent" @@ -41,6 +42,9 @@ labels: job: node_selector_key: openstack-control-plane node_selector_value: enabled + test: + node_selector_key: openstack-control-plane + node_selector_value: enabled network: dashboard: @@ -1979,6 +1983,8 @@ dependencies: service: oslo_cache - endpoint: internal service: oslo_db + - endpoint: internal + service: identity db_drop: services: - endpoint: internal @@ -1997,6 +2003,10 @@ dependencies: services: - endpoint: internal service: local_image_registry + tests: + services: + - endpoint: internal + service: dashboard pod: security_context: @@ -2031,6 +2041,11 @@ pod: horizon: volumeMounts: volumes: + horizon_tests: + init_container: null + horizon_tests: + volumeMounts: + volumes: replicas: server: 1 lifecycle: @@ -2085,9 +2100,18 @@ pod: limits: memory: "1024Mi" cpu: "2000m" + tests: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "1024Mi" + cpu: "2000m" # Names of secrets used by bootstrap and environmental checks secrets: + identity: + admin: horizon-keystone-admin oslo_db: admin: horizon-db-admin horizon: horizon-db-user @@ -2115,6 +2139,14 @@ endpoints: node: 5000 identity: name: keystone + auth: + admin: + region_name: RegionOne + username: admin + password: password + project_name: admin + user_domain_name: default + project_domain_name: default hosts: default: keystone internal: keystone-api @@ -2215,8 +2247,10 @@ manifests: job_db_drop: false job_image_repo_sync: true pdb: true + pod_helm_test: true network_policy: false secret_db: true secret_ingress_tls: true + secret_keystone: true service_ingress: true service: true diff --git a/tools/deployment/component/horizon/horizon.sh b/tools/deployment/component/horizon/horizon.sh new file mode 100755 index 0000000000..e553f16b5a --- /dev/null +++ b/tools/deployment/component/horizon/horizon.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Copyright 2019 The Openstack-Helm Authors. +# +# 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. + +set -xe + +#NOTE: Lint and package chart +make horizon + +#NOTE: Deploy command +: ${OSH_EXTRA_HELM_ARGS:=""} +helm upgrade --install horizon ./horizon \ + --namespace=openstack \ + ${OSH_EXTRA_HELM_ARGS} \ + ${OSH_EXTRA_HELM_ARGS_HORIZON} + +#NOTE: Wait for deploy +./tools/deployment/common/wait-for-pods.sh openstack + +#NOTE: Validate Deployment info +helm status horizon + +helm test horizon diff --git a/tools/deployment/developer/common/100-horizon.sh b/tools/deployment/developer/common/100-horizon.sh index 3c241310df..2913f9df41 100755 --- a/tools/deployment/developer/common/100-horizon.sh +++ b/tools/deployment/developer/common/100-horizon.sh @@ -33,3 +33,5 @@ helm upgrade --install horizon ./horizon \ #NOTE: Validate Deployment info helm status horizon + +helm test horizon diff --git a/zuul.d/jobs-openstack-helm.yaml b/zuul.d/jobs-openstack-helm.yaml index f86c87c803..388d14e3b9 100644 --- a/zuul.d/jobs-openstack-helm.yaml +++ b/zuul.d/jobs-openstack-helm.yaml @@ -211,6 +211,22 @@ - ./nova/values_overrides/opensuse_15.yaml - ./nova/values_overrides/rocky-opensuse_15.yaml +- job: + name: openstack-helm-horizon + parent: openstack-helm-chart-deploy + run: tools/gate/playbooks/osh-gate-runner.yaml + vars: + gate_scripts: + - ./tools/deployment/common/install-packages.sh + - ./tools/deployment/common/deploy-k8s.sh + - ./tools/deployment/common/setup-client.sh + - ./tools/deployment/component/common/ingress.sh + - ./tools/deployment/component/common/mariadb.sh + - ./tools/deployment/component/common/memcached.sh + - ./tools/deployment/component/common/rabbitmq.sh + - ./tools/deployment/component/keystone/keystone.sh + - ./tools/deployment/component/horizon/horizon.sh + - job: name: openstack-helm-apparmor parent: openstack-helm-chart-deploy diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml index 6a880d1d0d..1e2d82e3fc 100644 --- a/zuul.d/project.yaml +++ b/zuul.d/project.yaml @@ -41,6 +41,7 @@ - openstack-helm-compute-kit - openstack-helm-compute-kit-rocky-opensuse_15: voting: false + - openstack-helm-horizon - openstack-helm-apparmor: voting: false gate: