diff --git a/charts/plane-enterprise/README.md b/charts/plane-enterprise/README.md
index 7305f38..23876b9 100644
--- a/charts/plane-enterprise/README.md
+++ b/charts/plane-enterprise/README.md
@@ -161,6 +161,28 @@
| services.rabbitmq.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of `rabbitmq`. This is useful for adding metadata or configuration hints to your resources. |
| services.rabbitmq.external_rabbitmq_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Ignoring all the above keys, set `services.rabbitmq.local_setup` to `false` and set this key with remote connection url. |
+### OpenSearch Setup
+
+| Setting | Default | Required | Description |
+| --------------------------------------- | :-------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| services.opensearch.local_setup | true | | Plane uses `opensearch` as the search and analytics engine. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. AWS OpenSearch Service or similar services). Set this to `true` when you choose to setup stateful deployment of `opensearch`. Mark it as `false` when using a remotely hosted service |
+| services.opensearch.image | opensearchproject/opensearch:3.3.2 | | Using this key, user must provide the docker image name to setup the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) |
+| services.opensearch.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `opensearch`. (must be set when `services.opensearch.local_setup=true`) |
+| services.opensearch.servicePort | 9200 | | This key sets the default port number to be used while setting up stateful deployment of `opensearch`. |
+| services.opensearch.volumeSize | 5Gi | | While setting up the stateful deployment, while creating the persistant volume, volume allocation size need to be provided. This key helps you set the volume allocation size. Unit of this value must be in Mi (megabyte) or Gi (gigabyte) |
+| services.opensearch.username | plane | | Credentials are requried to access the hosted stateful deployment of `opensearch`. Use this key to set the username for the stateful deployment. |
+| services.opensearch.password | Secure@Pass#123!%^&* | | Credentials are requried to access the hosted stateful deployment of `opensearch`. Use this key to set the password for the stateful deployment. |
+| services.opensearch.assign_cluster_ip | false | | Set it to `true` if you want to assign `ClusterIP` to the service |
+| services.opensearch.nodeSelector | {} | | This key allows you to set the node selector for the stateful deployment of `opensearch`. This is useful when you want to run the deployment on specific nodes in your Kubernetes cluster. |
+| services.opensearch.tolerations | [] | | This key allows you to set the tolerations for the stateful deployment of `opensearch`. This is useful when you want to run the deployment on nodes with specific taints in your Kubernetes cluster. |
+| services.opensearch.affinity | {} | | This key allows you to set the affinity rules for the stateful deployment of `opensearch`. This is useful when you want to control how pods are scheduled on nodes in your Kubernetes cluster. |
+| services.opensearch.labels | {} | | This key allows you to set custom labels for the stateful deployment of `opensearch`. This is useful for organizing and selecting resources in your Kubernetes cluster. |
+| services.opensearch.annotations | {} | | This key allows you to set custom annotations for the stateful deployment of `opensearch`. This is useful for adding metadata or configuration hints to your resources. |
+| env.opensearch_remote_url | | | Users can also decide to use the remote hosted service and link to Plane deployment. Ignoring all the above keys, set `services.opensearch.local_setup` to `false` and set this key with remote connection url. |
+| env.opensearch_remote_username | | | Username for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. Note: This is not a secret and should be configured in values.yaml, not in external secrets. |
+| env.opensearch_remote_password | | | Password for remote OpenSearch service. Required when `services.opensearch.local_setup=false` and `env.opensearch_remote_url` is set. This can be configured in values.yaml or provided via external secrets (`opensearch_existingSecret` with `OPENSEARCH_PASSWORD`). |
+| env.opensearch_index_prefix | | | Prefix to be used for OpenSearch indices. This helps organize indices in a multi-tenant or multi-environment setup. |
+
### Doc Store (Minio/S3) Setup
| Setting | Default | Required | Description |
@@ -507,6 +529,8 @@ To configure the external secrets for your application, you need to define speci
| pgdb_existingSecret | `POSTGRES_PASSWORD` | Required if `postgres.local_setup=true` | Password for PostgreSQL database | `plane` |
| | `POSTGRES_DB` | Required if `postgres.local_setup=true` | Name of the PostgreSQL database | `plane` |
| | `POSTGRES_USER` | Required if `postgres.local_setup=true` | PostgreSQL user | `plane` |
+| opensearch_existingSecret | `OPENSEARCH_PASSWORD` | Required if OpenSearch is enabled | Password for OpenSearch | **local setup**: `Secure@Pass#123!%^&*`
**remote setup**: `your_remote_password` |
+| | `OPENSEARCH_INITIAL_ADMIN_PASSWORD` | Required if `opensearch.local_setup=true` | Initial admin password for local OpenSearch | `Secure@Pass#123!%^&*` |
| doc_store_existingSecret | `USE_MINIO` | Yes | Flag to enable MinIO as the storage backend | `1` |
| | `MINIO_ROOT_USER` | Yes | MinIO root user | `admin` |
| | `MINIO_ROOT_PASSWORD` | Yes | MinIO root password | `password` |
@@ -520,6 +544,11 @@ To configure the external secrets for your application, you need to define speci
| | `REDIS_URL` | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` |
| | `DATABASE_URL` | Yes | PostgreSQL connection URL | **k8s service example**: `postgresql://plane:plane@plane-pgdb.plane-ns.svc.cluster.local:5432/plane`
**external service example**: `postgresql://username:password@your-db-host:5432/plane` |
| | `AMQP_URL` | Yes | RabbitMQ connection URL | **k8s service example**: `amqp://plane:plane@plane-rabbitmq.plane-ns.svc.cluster.local:5672/`
**external service example**: `amqp://username:password@your-rabbitmq-host:5672/` |
+| | `OPENSEARCH_PASSWORD` | Required if OpenSearch is enabled | Password for OpenSearch | **local setup**: `Secure@Pass#123!%^&*`
**remote setup**: `your_remote_password` |
+| | `OPENSEARCH_ENABLED` | Yes | Flag to enable OpenSearch | `1` (enabled) or `0` (disabled) |
+| | `OPENSEARCH_URL` | Required if OpenSearch is enabled | OpenSearch connection URL | **k8s service example**: `http://plane-opensearch.plane-ns.svc.cluster.local:9200`
**external service example**: `https://your-opensearch-host:9200` |
+| | `OPENSEARCH_USERNAME` | Required if OpenSearch is enabled | Username for OpenSearch | **local setup**: `plane`
**remote setup**: `your_remote_username` |
+| | `OPENSEARCH_INDEX_PREFIX` | Optional | Prefix for OpenSearch indices | `plane_` |
| live_env_existingSecret | `REDIS_URL` | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` |
| silo_env_existingSecret | `SILO_HMAC_SECRET_KEY` | Yes | Silo HMAC secret Key | `` |
| | `REDIS_URL` | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` |
diff --git a/charts/plane-enterprise/questions.yml b/charts/plane-enterprise/questions.yml
index 63bd2ae..ccfeef3 100644
--- a/charts/plane-enterprise/questions.yml
+++ b/charts/plane-enterprise/questions.yml
@@ -895,6 +895,72 @@ questions:
type: string
show_if: "services.rabbitmq.local_setup=false"
+- variable: services.opensearch.local_setup
+ label: "Install OpenSearch"
+ type: boolean
+ default: true
+ group: "OpenSearch Setup"
+ # show_subquestion_if: true
+ subquestions:
+ - variable: services.opensearch.image
+ label: "Docker Image"
+ type: string
+ default: "opensearchproject/opensearch:3.3.2"
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.pullPolicy
+ label: "OpenSearch Pull Policy"
+ type: enum
+ options:
+ - "Always"
+ - "IfNotPresent"
+ - "Never"
+ default: "IfNotPresent"
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.servicePort
+ label: Service Port
+ type: int
+ default: 9200
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.volumeSize
+ label: "Volume Size"
+ type: string
+ default: "5Gi"
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.username
+ label: "Username"
+ type: string
+ default: "plane"
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.password
+ label: "Password"
+ type: password
+ default: "Secure@Pass#123!%^&*"
+ show_if: "services.opensearch.local_setup=true"
+ - variable: services.opensearch.assign_cluster_ip
+ label: "Assign Cluster IP"
+ type: boolean
+ default: false
+ show_if: "services.opensearch.local_setup=true"
+ - variable: env.opensearch_remote_url
+ label: "OpenSearch Remote URL"
+ type: string
+ default: ""
+ show_if: "services.opensearch.local_setup=false"
+ - variable: env.opensearch_remote_username
+ label: "OpenSearch Remote Username"
+ type: string
+ default: ""
+ show_if: "services.opensearch.local_setup=false"
+ - variable: env.opensearch_remote_password
+ label: "OpenSearch Remote Password"
+ type: password
+ default: ""
+ show_if: "services.opensearch.local_setup=false"
+ - variable: env.opensearch_index_prefix
+ label: "Index Prefix"
+ type: string
+ default: ""
+
- variable: services.minio.local_setup
label: "Install Minio"
type: boolean
diff --git a/charts/plane-enterprise/templates/config-secrets/app-env.yaml b/charts/plane-enterprise/templates/config-secrets/app-env.yaml
index 0d8d113..62094e9 100644
--- a/charts/plane-enterprise/templates/config-secrets/app-env.yaml
+++ b/charts/plane-enterprise/templates/config-secrets/app-env.yaml
@@ -36,6 +36,13 @@ stringData:
AWS_CA_BUNDLE: "/s3-custom-ca/{{ .Values.airgapped.s3SecretKey }}"
{{- end }}
+ {{- if .Values.services.opensearch.local_setup }}
+ OPENSEARCH_PASSWORD: {{ .Values.services.opensearch.password | default "Secure@Pass#123!%^&*" | quote }}
+ {{- else if .Values.env.opensearch_remote_url }}
+ OPENSEARCH_PASSWORD: {{ .Values.env.opensearch_remote_password | default "" | quote }}
+ {{- else }}
+ OPENSEARCH_PASSWORD: ""
+ {{- end }}
{{- end }}
---
@@ -78,4 +85,21 @@ data:
CORS_ALLOWED_ORIGINS: "http://{{ .Values.license.licenseDomain }},https://{{ .Values.license.licenseDomain }},{{ .Values.env.cors_allowed_origins }}"
{{- else}}
CORS_ALLOWED_ORIGINS: "http://{{ .Values.license.licenseDomain }},https://{{ .Values.license.licenseDomain }}"
+ {{- end }}
+
+ {{- if .Values.services.opensearch.local_setup }}
+ OPENSEARCH_ENABLED: "1"
+ OPENSEARCH_URL: "http://{{ .Release.Name }}-opensearch.{{ .Release.Namespace }}.svc.cluster.local:9200"
+ OPENSEARCH_USERNAME: {{ .Values.services.opensearch.username | default "plane" | quote }}
+ OPENSEARCH_INDEX_PREFIX: {{ .Values.env.opensearch_index_prefix | default "" | quote }}
+ {{- else if .Values.env.opensearch_remote_url }}
+ OPENSEARCH_ENABLED: "1"
+ OPENSEARCH_URL: {{ .Values.env.opensearch_remote_url | quote }}
+ OPENSEARCH_USERNAME: {{ .Values.env.opensearch_remote_username | default "" | quote }}
+ OPENSEARCH_INDEX_PREFIX: {{ .Values.env.opensearch_index_prefix | default "" | quote }}
+ {{- else }}
+ OPENSEARCH_ENABLED: "0"
+ OPENSEARCH_URL: ""
+ OPENSEARCH_USERNAME: ""
+ OPENSEARCH_INDEX_PREFIX: ""
{{- end }}
\ No newline at end of file
diff --git a/charts/plane-enterprise/templates/config-secrets/opensearchdb.yaml b/charts/plane-enterprise/templates/config-secrets/opensearchdb.yaml
new file mode 100644
index 0000000..32a2310
--- /dev/null
+++ b/charts/plane-enterprise/templates/config-secrets/opensearchdb.yaml
@@ -0,0 +1,68 @@
+{{- if .Values.services.opensearch.local_setup }}
+{{- if (empty .Values.external_secrets.opensearch_existingSecret) }}
+apiVersion: v1
+kind: Secret
+type: Opaque
+metadata:
+ namespace: {{ .Release.Namespace }}
+ name: {{ .Release.Name }}-opensearch-secrets
+stringData:
+ OPENSEARCH_PASSWORD: {{ .Values.services.opensearch.password | default "Secure@Pass#123!%^&*" | quote }}
+ OPENSEARCH_INITIAL_ADMIN_PASSWORD: {{ .Values.services.opensearch.password | default "Secure@Pass#123!%^&*" | quote }}
+{{- end }}
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ namespace: {{ .Release.Namespace }}
+ name: {{ .Release.Name }}-opensearch-vars
+data:
+ OPENSEARCH_USERNAME: {{ .Values.services.opensearch.username | default "plane" | quote }}
+ create-user.sh: |
+ #!/bin/bash
+
+ echo "=== OpenSearch Initialization Script ==="
+
+ echo "Modifying internal_users.yml before starting OpenSearch..."
+
+ # Hardcoded values
+ export OPENSEARCH_USER=${OPENSEARCH_USER:-"plane"}
+ export OPENSEARCH_PASSWORD=${OPENSEARCH_PASSWORD:-"Secure@Pass#123!%^&*"}
+
+ echo "=== Configuration ==="
+ echo "USER: ${OPENSEARCH_USER}"
+ echo "PASSWORD: **********"
+ echo ""
+
+ # Run the user creation script before OpenSearch starts
+ export HASHED_PASSWORD=$(bash /usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p "${OPENSEARCH_PASSWORD}")
+
+ # Path to internal users file
+ INTERNAL_USERS_FILE="/usr/share/opensearch/config/opensearch-security/internal_users.yml"
+
+ # Ensure the directory exists
+ mkdir -p "$(dirname "$INTERNAL_USERS_FILE")"
+
+ # Check if user already exists
+ if grep -q "^${OPENSEARCH_USER}:" "$INTERNAL_USERS_FILE"; then
+ echo "User ${OPENSEARCH_USER} already exists in internal_users.yml"
+ else
+ echo "Adding user ${OPENSEARCH_USER} to internal_users.yml"
+ cat << EOF >> "$INTERNAL_USERS_FILE"
+
+ ${OPENSEARCH_USER}:
+ hash: "${HASHED_PASSWORD}"
+ reserved: false
+ backend_roles:
+ - "admin"
+ description: "User for Plane"
+ EOF
+ echo "User ${OPENSEARCH_USER} added successfully to configuration"
+ fi
+
+ echo "Starting OpenSearch..."
+ # Start OpenSearch with the original entrypoint
+ exec /usr/share/opensearch/opensearch-docker-entrypoint.sh
+
+{{- end }}
+
diff --git a/charts/plane-enterprise/templates/workloads/opensearch.stateful.yaml b/charts/plane-enterprise/templates/workloads/opensearch.stateful.yaml
new file mode 100644
index 0000000..bd298ef
--- /dev/null
+++ b/charts/plane-enterprise/templates/workloads/opensearch.stateful.yaml
@@ -0,0 +1,107 @@
+{{- if .Values.services.opensearch.local_setup }}
+
+apiVersion: v1
+kind: Service
+metadata:
+ namespace: {{ .Release.Namespace }}
+ name: {{ .Release.Name }}-opensearch
+ labels:
+ app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-opensearch
+spec:
+ type: ClusterIP
+ {{- if not .Values.services.opensearch.assign_cluster_ip }}
+ clusterIP: None
+ {{- end }}
+ ports:
+ - name: opensearch-{{ .Values.services.opensearch.servicePort }}
+ port: {{ .Values.services.opensearch.servicePort }}
+ protocol: TCP
+ targetPort: 9200
+ selector:
+ app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-opensearch
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ namespace: {{ .Release.Namespace }}
+ name: {{ .Release.Name }}-opensearch-wl
+ {{- include "plane.labelsAndAnnotations" .Values.services.opensearch }}
+spec:
+ selector:
+ matchLabels:
+ app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-opensearch
+ serviceName: {{ .Release.Name }}-opensearch
+ template:
+ metadata:
+ labels:
+ app.name: {{ .Release.Namespace }}-{{ .Release.Name }}-opensearch
+ spec:
+ {{- include "plane.podScheduling" .Values.services.opensearch }}
+ securityContext:
+ fsGroup: 1000
+ containers:
+ - image: {{ .Values.services.opensearch.image }}
+ imagePullPolicy: {{ .Values.services.opensearch.pullPolicy | default "IfNotPresent" }}
+ name: {{ .Release.Name }}-opensearch
+ stdin: true
+ tty: true
+ securityContext:
+ runAsUser: 1000
+ runAsNonRoot: true
+ envFrom:
+ - secretRef:
+ name: {{ if not (empty .Values.external_secrets.opensearch_existingSecret) }}{{ .Values.external_secrets.opensearch_existingSecret }}{{ else }}{{ .Release.Name }}-opensearch-secrets{{ end }}
+ optional: false
+ - configMapRef:
+ name: {{ .Release.Name }}-opensearch-vars
+ optional: false
+ env:
+ - name: discovery.type
+ value: "single-node"
+ - name: bootstrap.memory_lock
+ value: "false"
+ - name: OPENSEARCH_JAVA_OPTS
+ value: "-Xms768m -Xmx768m"
+ - name: OPENSEARCH_USER
+ valueFrom:
+ configMapKeyRef:
+ name: {{ .Release.Name }}-opensearch-vars
+ key: OPENSEARCH_USERNAME
+ {{- if .Values.extraEnv }}
+ {{- toYaml .Values.extraEnv | nindent 10 }}
+ {{- end }}
+ command: ["/bin/bash", "/docker-entrypoint-init.d/create-user.sh"]
+ volumeMounts:
+ - mountPath: /usr/share/opensearch/data
+ name: pvc-{{ .Release.Name }}-opensearch-vol
+ subPath: ''
+ - mountPath: /docker-entrypoint-init.d
+ name: {{.Release.Name}}-opensearch-init-script
+ serviceAccount: {{ .Release.Name }}-srv-account
+ serviceAccountName: {{ .Release.Name }}-srv-account
+ volumes:
+ - name: {{.Release.Name}}-opensearch-init-script
+ configMap:
+ name: {{ .Release.Name }}-opensearch-vars
+ defaultMode: 0755
+ items:
+ - key: create-user.sh
+ path: create-user.sh
+ volumeClaimTemplates:
+ - apiVersion: v1
+ kind: PersistentVolumeClaim
+ metadata:
+ creationTimestamp: null
+ namespace: {{ .Release.Namespace }}
+ name: pvc-{{ .Release.Name }}-opensearch-vol
+ spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: {{ .Values.services.opensearch.volumeSize | default "5Gi" | quote }}
+ storageClassName: {{ .Values.env.storageClass | quote }}
+ volumeMode: Filesystem
+
+{{- end }}
+
diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml
index cd44316..618c7e4 100644
--- a/charts/plane-enterprise/values.yaml
+++ b/charts/plane-enterprise/values.yaml
@@ -80,6 +80,21 @@ services:
labels: {}
annotations: {}
+ opensearch:
+ local_setup: true
+ image: opensearchproject/opensearch:3.3.2
+ servicePort: 9200
+ volumeSize: 5Gi
+ pullPolicy: IfNotPresent
+ username: plane
+ password: Secure@Pass#123!%^&*
+ assign_cluster_ip: false
+ nodeSelector: {}
+ tolerations: []
+ affinity: {}
+ labels: {}
+ annotations: {}
+
minio:
local_setup: true
image: minio/minio:latest
@@ -308,6 +323,7 @@ external_secrets:
# Name of the existing Kubernetes Secret resource; see README for more details
rabbitmq_existingSecret: ''
pgdb_existingSecret: ''
+ opensearch_existingSecret: ''
doc_store_existingSecret: ''
app_env_existingSecret: ''
live_env_existingSecret: ''
@@ -340,6 +356,12 @@ env:
secret_key: "60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5"
api_key_rate_limit: "60/minute"
+ #OPENSEARCH ENVS
+ opensearch_remote_url: ''
+ opensearch_remote_username: ''
+ opensearch_remote_password: ''
+ opensearch_index_prefix: ''
+
sentry_dsn: ''
sentry_environment: ''