diff --git a/tests/templates/kuttl/opensearch-dashboards/00-patch-ns.yaml b/tests/templates/kuttl/opensearch-dashboards/00-patch-ns.yaml new file mode 100644 index 0000000..d4f91fa --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/00-patch-ns.yaml @@ -0,0 +1,15 @@ +# see https://github.com/stackabletech/issues/issues/566 +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + kubectl patch namespace $NAMESPACE --patch=' + { + "metadata": { + "labels": { + "pod-security.kubernetes.io/enforce": "privileged" + } + } + }' + timeout: 120 diff --git a/tests/templates/kuttl/opensearch-dashboards/01-rbac.yaml b/tests/templates/kuttl/opensearch-dashboards/01-rbac.yaml new file mode 100644 index 0000000..64eced8 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/01-rbac.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: test-service-account +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: test-role +rules: + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - privileged + verbs: + - use +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: test-role-binding +subjects: + - kind: ServiceAccount + name: test-service-account +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: test-role diff --git a/tests/templates/kuttl/opensearch-dashboards/02-limit-range.yaml b/tests/templates/kuttl/opensearch-dashboards/02-limit-range.yaml new file mode 100644 index 0000000..b1789b2 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/02-limit-range.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: limit-request-ratio +spec: + limits: + - type: Container + maxLimitRequestRatio: + cpu: 5 + memory: 1 diff --git a/tests/templates/kuttl/opensearch-dashboards/10-assert.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/10-assert.yaml.j2 new file mode 100644 index 0000000..96e9254 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/10-assert.yaml.j2 @@ -0,0 +1,23 @@ +# All fields are checked that are set by the operator. +# This helps to detect unintentional changes. It is also a good reference for the output of the +# operator. The maintenance effort should be okay as long as it is only done in the smoke test. +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: opensearch-nodes-cluster-manager +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: opensearch-nodes-data +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/opensearch-dashboards/10-install-opensearch.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/10-install-opensearch.yaml.j2 new file mode 100644 index 0000000..8851a8f --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/10-install-opensearch.yaml.j2 @@ -0,0 +1,213 @@ +--- +apiVersion: opensearch.stackable.tech/v1alpha1 +kind: OpenSearchCluster +metadata: + name: opensearch +spec: + image: +{% if test_scenario['values']['opensearch'].find(",") > 0 %} + custom: "{{ test_scenario['values']['opensearch'].split(',')[1] }}" + productVersion: "{{ test_scenario['values']['opensearch'].split(',')[0] }}" +{% else %} + productVersion: "{{ test_scenario['values']['opensearch'] }}" +{% endif %} + pullPolicy: IfNotPresent + nodes: + roleGroups: + cluster-manager: + config: + nodeRoles: + - cluster_manager + resources: + storage: + data: + capacity: 100Mi + listenerClass: external-stable + replicas: 1 + podOverrides: + spec: + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/scope: node,pod,service=opensearch,service=opensearch-nodes-cluster-manager-headless + data: + config: + nodeRoles: + - ingest + - data + - remote_cluster_client + resources: + storage: + data: + capacity: 2Gi + listenerClass: cluster-internal + replicas: 1 + podOverrides: + spec: + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/scope: node,pod,service=opensearch-nodes-data-headless + envOverrides: + # Only required for the official image + # The official image (built with https://github.com/opensearch-project/opensearch-build) + # installs a demo configuration if not disabled explicitly. + DISABLE_INSTALL_DEMO_CONFIG: "true" + OPENSEARCH_HOME: {{ test_scenario['values']['opensearch_home'] }} + configOverrides: + opensearch.yml: + # Disable memory mapping in this test; If memory mapping were activated, the kernel setting + # vm.max_map_count would have to be increased to 262144 on the node. + node.store.allow_mmap: "false" + # Disable the disk allocation decider in this test; Otherwise the test depends on the disk + # usage of the node and if the relative watermark set in + # `cluster.routing.allocation.disk.watermark.high` is reached then the security index could + # not be created even if enough disk space would be available. + cluster.routing.allocation.disk.threshold_enabled: "false" + plugins.security.allow_default_init_securityindex: "true" + plugins.security.ssl.transport.enabled: "true" + plugins.security.ssl.transport.pemcert_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/tls.crt + plugins.security.ssl.transport.pemkey_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/tls.key + plugins.security.ssl.transport.pemtrustedcas_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/ca.crt + plugins.security.ssl.http.enabled: "true" + plugins.security.ssl.http.pemcert_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/tls.crt + plugins.security.ssl.http.pemkey_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/tls.key + plugins.security.ssl.http.pemtrustedcas_filepath: {{ test_scenario['values']['opensearch_home'] }}/config/tls/ca.crt + podOverrides: + spec: + containers: + - name: opensearch + volumeMounts: + - name: security-config + mountPath: {{ test_scenario['values']['opensearch_home'] }}/config/opensearch-security + readOnly: true + - name: tls + mountPath: {{ test_scenario['values']['opensearch_home'] }}/config/tls + readOnly: true + volumes: + - name: security-config + secret: + secretName: opensearch-security-config + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: tls + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" +--- +apiVersion: v1 +kind: Secret +metadata: + name: opensearch-credentials +data: + admin: QUpWRnNHSkJicFQ2bUNobg== # AJVFsGJBbpT6mChn + kibanaserver: RTRrRU51RW1rcUgzanlIQw== # E4kENuEmkqH3jyHC +--- +apiVersion: v1 +kind: Secret +metadata: + name: opensearch-security-config +stringData: + action_groups.yml: | + --- + _meta: + type: actiongroups + config_version: 2 + allowlist.yml: | + --- + _meta: + type: allowlist + config_version: 2 + + config: + enabled: false + audit.yml: | + --- + _meta: + type: audit + config_version: 2 + + config: + enabled: false + config.yml: | + --- + _meta: + type: config + config_version: 2 + + config: + dynamic: + authc: + basic_internal_auth_domain: + description: Authenticate via HTTP Basic against internal users database + http_enabled: true + transport_enabled: true + order: 1 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: intern + authz: {} + internal_users.yml: | + --- + # The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh + + _meta: + type: internalusers + config_version: 2 + + admin: + hash: $2y$10$xRtHZFJ9QhG9GcYhRpAGpufCZYsk//nxsuel5URh0GWEBgmiI4Q/e + reserved: true + backend_roles: + - admin + description: OpenSearch admin user + + kibanaserver: + hash: $2y$10$vPgQ/6ilKDM5utawBqxoR.7euhVQ0qeGl8mPTeKhmFT475WUDrfQS + reserved: true + description: OpenSearch Dashboards user + nodes_dn.yml: | + --- + _meta: + type: nodesdn + config_version: 2 + roles.yml: | + --- + _meta: + type: roles + config_version: 2 + roles_mapping.yml: | + --- + _meta: + type: rolesmapping + config_version: 2 + + all_access: + reserved: false + backend_roles: + - admin + + kibana_server: + reserved: true + users: + - kibanaserver + tenants.yml: | + --- + _meta: + type: tenants + config_version: 2 diff --git a/tests/templates/kuttl/opensearch-dashboards/20-assert.yaml b/tests/templates/kuttl/opensearch-dashboards/20-assert.yaml new file mode 100644 index 0000000..becb010 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/20-assert.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opensearch-dashboards +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/opensearch-dashboards/20-install-opensearch-dashboards.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/20-install-opensearch-dashboards.yaml.j2 new file mode 100644 index 0000000..3a9f221 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/20-install-opensearch-dashboards.yaml.j2 @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: > + helm install opensearch-dashboards opensearch-dashboards + --repo https://opensearch-project.github.io/helm-charts + --version "{{ test_scenario['values']['opensearch'].split(',')[0] }}" + --values 20_opensearch-dashboards-values.yaml + --namespace $NAMESPACE + --wait + timeout: 600 diff --git a/tests/templates/kuttl/opensearch-dashboards/20_opensearch-dashboards-values.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/20_opensearch-dashboards-values.yaml.j2 new file mode 100644 index 0000000..eb594cb --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/20_opensearch-dashboards-values.yaml.j2 @@ -0,0 +1,17 @@ +opensearchHosts: https://opensearch-nodes-cluster-manager:9200 +image: + repository: oci.stackable.tech/sdp/opensearch-dashboards + tag: {{ test_scenario['values']['opensearch'].split(',')[0] }}-stackable0.0.0-dev +service: + type: NodePort +serviceAccount: + # The Helm chart appends "-dashboards", so that the actual + # ServiceAccount name is "opensearch-dashboards". + # see https://github.com/opensearch-project/helm-charts/blob/opensearch-dashboards-2.27.1/charts/opensearch-dashboards/templates/_helpers.tpl#L58 + name: opensearch +extraEnvs: + - name: OPENSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: opensearch-credentials + key: kibanaserver diff --git a/tests/templates/kuttl/opensearch-dashboards/30-create-configmap.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/30-create-configmap.yaml.j2 new file mode 100644 index 0000000..51b0321 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/30-create-configmap.yaml.j2 @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: kubectl create cm test-script -n $NAMESPACE --from-file=test.py diff --git a/tests/templates/kuttl/opensearch-dashboards/31-assert.yaml b/tests/templates/kuttl/opensearch-dashboards/31-assert.yaml new file mode 100644 index 0000000..5898777 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/31-assert.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: install-test-container +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: python +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/opensearch-dashboards/31-install-test-container.yaml.j2 b/tests/templates/kuttl/opensearch-dashboards/31-install-test-container.yaml.j2 new file mode 100644 index 0000000..0ca95e8 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/31-install-test-container.yaml.j2 @@ -0,0 +1,86 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: python +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: python +{% if test_scenario['values']['openshift'] == 'true' %} +rules: +- apiGroups: ["security.openshift.io"] + resources: ["securitycontextconstraints"] + resourceNames: ["privileged"] + verbs: ["use"] +{% endif %} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: python +subjects: + - kind: ServiceAccount + name: python +roleRef: + kind: Role + name: python + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +metadata: + name: install-test-container +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: python + labels: + app: python +spec: + replicas: 1 + selector: + matchLabels: + app: python + template: + metadata: + labels: + app: python + spec: + serviceAccountName: python + containers: + - name: osd-test + image: oci.stackable.tech/sdp/testing-tools:0.2.0-stackable0.0.0-dev + stdin: true + tty: true + resources: + requests: + memory: "128Mi" + cpu: "512m" + limits: + memory: "128Mi" + cpu: "1" + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: OPENSEARCH_USER + value: admin + - name: OPENSEARCH_PASSWORD + valueFrom: + secretKeyRef: + name: opensearch-credentials + key: admin + volumeMounts: + - name: test-script + mountPath: /tmp/test-script + terminationGracePeriodSeconds: 1 + volumes: + - name: test-script + configMap: + name: test-script + terminationGracePeriodSeconds: 1 diff --git a/tests/templates/kuttl/opensearch-dashboards/35-assert.yaml b/tests/templates/kuttl/opensearch-dashboards/35-assert.yaml new file mode 100644 index 0000000..5c33fc5 --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/35-assert.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: test +timeout: 300 +commands: + - script: |- + sleep 60 # After the OpenSearch Dashboards container has become ready it still takes some time until the application has completely started. + kubectl exec -n $NAMESPACE python-0 -- python /tmp/test-script/test.py diff --git a/tests/templates/kuttl/opensearch-dashboards/test.py b/tests/templates/kuttl/opensearch-dashboards/test.py new file mode 100644 index 0000000..7d18a5b --- /dev/null +++ b/tests/templates/kuttl/opensearch-dashboards/test.py @@ -0,0 +1,89 @@ +import logging +import os +import requests +import sys +import urllib3 + +# disable tls insecure warnings +urllib3.disable_warnings() + +logging.basicConfig( + level="INFO", format="%(asctime)s %(levelname)s: %(message)s", stream=sys.stdout +) + +namespace = os.environ["NAMESPACE"] +opensearch_user = os.environ["OPENSEARCH_USER"] +opensearch_password = os.environ["OPENSEARCH_PASSWORD"] +opensearch_dashboards_service = "http://opensearch-dashboards:5601" + +session = requests.Session() +session.headers.update({"osd-xsrf": "true"}) + +login_page = session.post( + f"{opensearch_dashboards_service}/auth/login", + data={"username": opensearch_user, "password": opensearch_password}, +) +assert login_page.ok, "Failed to login to OpenSearch Dashboards" + +api_status = session.get(f"{opensearch_dashboards_service}/api/status") +assert api_status.ok, "Failed to get API status" + +opensearch_version = api_status.json()["version"]["number"] + +assert api_status.json()["status"]["overall"]["state"] == "green", ( + "Overall state of OpenSearch Dashboards is not green" +) + +# Check if all expected plugins are present and working +expected_plugins = [ + "alertingDashboards", + "anomalyDetectionDashboards", + "assistantDashboards", + "customImportMapDashboards", + "flowFrameworkDashboards", + "indexManagementDashboards", + "mlCommonsDashboards", + "notificationsDashboards", + "observabilityDashboards", + "queryInsightsDashboards", + "queryWorkbenchDashboards", + "reportsDashboards", + "searchRelevanceDashboards", + "securityAnalyticsDashboards", + "securityDashboards", +] + +states = {} +for status in api_status.json()["status"]["statuses"]: + states[ + status["id"].removeprefix("plugin:").removesuffix(f"@{opensearch_version}") + ] = status["state"] == "green" +for plugin in expected_plugins: + assert plugin in states and states[plugin], ( + f"Expected plugin {plugin} not present or working." + ) + +# Load Sample Data (web logs & flights) +sample_web_logs = session.post(f"{opensearch_dashboards_service}/api/sample_data/logs") +assert sample_web_logs.ok, "Failed to create sample data (logs)" + +sample_flights = session.post( + f"{opensearch_dashboards_service}/api/sample_data/flights" +) +assert sample_flights.ok, "Failed to create sample data (flights)" + +# Check that the indices were created +indices = session.get( + f"{opensearch_dashboards_service}/api/saved_objects/_find?fields=title&per_page=10000&type=index-pattern" +) +assert indices.ok, "Failed to get indices" + +logs_index = indices.json()["saved_objects"][0] +assert logs_index["attributes"]["title"] == "opensearch_dashboards_sample_data_logs", ( + "First index should be sample logs" +) + +flights_index = indices.json()["saved_objects"][1] +assert ( + flights_index["attributes"]["title"] == "opensearch_dashboards_sample_data_flights" +), "Second index should be sample flights" diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index efad126..66560af 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -35,6 +35,11 @@ tests: - opensearch - openshift - opensearch_home + - name: opensearch-dashboards + dimensions: + - opensearch + - openshift + - opensearch_home suites: - name: nightly patch: @@ -44,6 +49,7 @@ suites: - name: smoke-latest select: - smoke + - opensearch-dashboards patch: - dimensions: - expr: last