Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/docs/containers/JIRA.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,26 @@ information, please refer to

Override the default AWS API endpoint with a custom one (optional).

### OpenSearch configuration

Starting with Jira 11.0, you can configure Jira to use OpenSearch as the search platform.
Copy link
Copy Markdown
Collaborator

@gweyeratlassian gweyeratlassian Mar 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: OpenSearch went GA with Jira 11.2.


* `ATL_SEARCH_PLATFORM`

The search platform to use. Set to `opensearch` if you want to use OpenSearch as the search platform.

* `ATL_OPENSEARCH_HTTP_URL`

HTTP(S) URL of the OpenSearch cluster, or multiple URLs separated by commas.

* `ATL_OPENSEARCH_USERNAME`

Username for the OpenSearch cluster.

* `ATL_OPENSEARCH_PASSWORD`

Password for the OpenSearch cluster.

### S3 Attachments storage configuration
Starting with Jira 9.9, you can configure Jira to [store attachment files in Amazon S3](https://confluence.atlassian.com/adminjiraserver/storing-attachments-in-amazon-s3-1282250191.html). For requirements and additional
information, please refer to [Configuring Amazon S3 Object Storage](https://confluence.atlassian.com/pages/viewpage.action?spaceKey=JSERVERM&title=.Configuring+Amazon+S3+object+storage+vJira_admin_9.9).
Expand Down
1 change: 1 addition & 0 deletions docs/docs/examples/.pages
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ nav:
- bamboo
- bitbucket
- confluence
- jira
- logging
- ...
4 changes: 4 additions & 0 deletions docs/docs/examples/jira/.pages
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
collapse_single_pages: false
nav:
- JIRA_OPENSEARCH.md
- ...
54 changes: 54 additions & 0 deletions docs/docs/examples/jira/JIRA_OPENSEARCH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Configuring OpenSearch for Jira

!!!info "Jira and Helm chart version"
OpenSearch is supported in Jira 11.0.0 and Helm chart 2.0.10 onwards.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: OpenSearch went GA with Jira 11.2.


As Jira instances grow in size and scale, the default search engine, Lucene, may be slower to index and return search results. To address this, Jira Data Center offers an alternative search engine as an opt-in feature — OpenSearch.

## Deploy OpenSearch Helm Chart with Jira

!!!warning "Support disclaimer"
Atlassian does not officially support OpenSearch Helm chart that can be installed with the Jira Helm release. Should you encounter any issues with the deployment, maintenance and upgrades, reach out to the [vendor](https://github.com/opensearch-project/helm-charts/tree/main/charts/opensearch){.external}.
Moreover, if you intend to deploy OpenSearch to a critical Kubernetes environment, make sure you follow all the best practices, i.e. deploy a multi node cluster, use taints and tolerations, affinity rules, sufficient resources requests, have DR and backup strategies etc.

## Deploy with the default settings

To deploy OpenSearch Helm chart and automatically configure Jira to use it as a search platform, set the following in your Helm values file:

```yaml
opensearch:
enabled: true
```
This will:

* auto-generate the initial OpenSearch admin password and create a Kubernetes secret with `OPENSEARCH_INITIAL_ADMIN_PASSWORD` key
* deploy [OpenSearch Helm chart](https://github.com/opensearch-project/helm-charts/tree/main/charts/opensearch){.external} to the target namespace with the default settings: single node, 1Gi memory/1 vCPU resources requests, 10Gi storage request
* configure Jira to use the deployed OpenSearch cluster by setting `ATL_SEARCH_PLATFORM=opensearch`, `ATL_OPENSEARCH_HTTP_URL=http://opensearch-cluster-master:9200`, `ATL_OPENSEARCH_USERNAME=admin` and `ATL_OPENSEARCH_PASSWORD` environment variables on the Jira container

## Override OpenSearch Helm chart values

You can configure your OpenSearch cluster and the deployment options by overriding any values that the [Helm chart](https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml){.external} exposes. OpenSearch values must be nested under `opensearch` stanza in your Helm values file, for example:

```yaml
opensearch:
singleNode: false
replicas: 5
config:
opensearch.yml: |
cluster.name: opensearch-cluster
```

## Use an existing OpenSearch secret

If you have a pre-created Kubernetes secret with the OpenSearch admin password, you can reference it instead of having the chart auto-generate one:

```yaml
opensearch:
enabled: true
credentials:
createSecret: false
existingSecretRef:
name: my-opensearch-secret
```

The secret must contain a key named `OPENSEARCH_INITIAL_ADMIN_PASSWORD`.
7 changes: 5 additions & 2 deletions src/main/charts/jira/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ dependencies:
- name: common
repository: https://atlassian.github.io/data-center-helm-charts
version: 1.2.7
digest: sha256:6dc6e131380a4f43edcaae60ee0f8341a463013d8460bd657ca798139d4f428a
generated: "2024-09-10T03:31:09.693286348Z"
- name: opensearch
repository: https://opensearch-project.github.io/helm-charts
version: 3.5.0
digest: sha256:1bd020af24c471b52a62f6b9330a0f1d94e27a60087fa48d6f1f152e91bec9ea
generated: "2026-03-13T14:50:45.468195+01:00"
4 changes: 4 additions & 0 deletions src/main/charts/jira/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ dependencies:
- name: common
version: 1.2.7
repository: https://atlassian.github.io/data-center-helm-charts
- name: opensearch
version: 3.5.0
repository: https://opensearch-project.github.io/helm-charts
condition: opensearch.enabled
38 changes: 38 additions & 0 deletions src/main/charts/jira/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -545,3 +545,41 @@ volumeClaimTemplates:
set -e; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts; chmod 664 /var/ssl/cacerts; for crt in /tmp/crt/*.*; do echo "Adding $crt to keystore"; keytool -import -keystore /var/ssl/cacerts -storepass changeit -noprompt -alias $(echo $(basename $crt)) -file $crt; done;
{{- end }}
{{- end }}

{{- define "generate_static_password_b64enc" -}}
{{- if not (index .Release "temp_vars") -}}
{{- $_ := set .Release "temp_vars" dict -}}
{{- end -}}
{{- $key := printf "%s_%s" .Release.Name "password" -}}
{{- if not (index .Release.temp_vars $key) -}}
{{- $_ := set .Release.temp_vars $key (randAlphaNum 40 | b64enc ) -}}
{{- end -}}
{{- index .Release.temp_vars $key -}}
{{- end -}}

{{- define "opensearch.initial.admin.password" }}
{{- $defaultSecretName := "opensearch-initial-password" }}
{{- $secretName := default $defaultSecretName .Values.opensearch.credentials.existingSecretRef.name }}
{{- $secretData := (lookup "v1" "Secret" .Release.Namespace $secretName) }}
{{- if $secretData.data }}
{{- index $secretData.data "OPENSEARCH_INITIAL_ADMIN_PASSWORD" }}
{{- else }}
{{ include "generate_static_password_b64enc" . }}
{{- end }}
{{- end }}

{{- define "opensearch.env.vars" }}
{{- if .Values.opensearch.enabled }}
- name: ATL_SEARCH_PLATFORM
value: opensearch
- name: ATL_OPENSEARCH_HTTP_URL
value: http://opensearch-cluster-master:9200
- name: ATL_OPENSEARCH_USERNAME
value: admin
- name: ATL_OPENSEARCH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.opensearch.credentials.existingSecretRef.name | default "opensearch-initial-password" }}
key: OPENSEARCH_INITIAL_ADMIN_PASSWORD
{{- end }}
{{- end }}
11 changes: 11 additions & 0 deletions src/main/charts/jira/templates/secret-opensearch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{- if and .Values.opensearch.enabled .Values.opensearch.credentials.createSecret (empty .Values.opensearch.credentials.existingSecretRef.name) }}
apiVersion: v1
kind: Secret
metadata:
name: opensearch-initial-password
labels:
{{- include "common.labels.commonLabels" . | nindent 4 }}
type: Opaque
data:
OPENSEARCH_INITIAL_ADMIN_PASSWORD: {{- include "opensearch.initial.admin.password" . | indent 4 }}
{{- end }}
20 changes: 20 additions & 0 deletions src/main/charts/jira/templates/statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@ spec:
{{- end }}
{{- end }}
initContainers:
{{- if .Values.opensearch.enabled }}
- name: opensearch-check
image: {{ include "jira.image" . | quote }}
command: ['sh', '-c']
args:
- |
timeout=300
end=$(($(date +%s) + timeout))
while [ $(date +%s) -lt $end ]; do
if curl -s -o /dev/null -w "%{http_code}" http://opensearch-cluster-master:9200 | grep -qE '^(200|401|403)$'; then
echo "OpenSearch is ready"
exit 0
fi
echo "OpenSearch server not ready or not reachable. Waiting..."
sleep 5
done
echo "OpenSearch did not become ready in ${timeout} seconds. Exiting"
exit 1
{{- end }}
{{- include "jira.additionalInitContainers" . | nindent 8 }}
{{- if and .Values.volumes.sharedHome.nfsPermissionFixer.enabled (not .Values.openshift.runWithRestrictedSCC) }}
- name: nfs-permission-fixer
Expand Down Expand Up @@ -168,6 +187,7 @@ spec:
- name: CATALINA_OPTS
value: {{ include "common.jmx.javaagent" . | replace "\n" "" | quote }}
{{- end }}
{{- include "opensearch.env.vars" . | nindent 12 }}
{{- include "jira.additionalEnvironmentVariables" . | nindent 12 }}
ports:
- name: http
Expand Down
34 changes: 34 additions & 0 deletions src/main/charts/jira/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1347,3 +1347,37 @@ openshift:
# configuration files as ConfigMaps.
#
runWithRestrictedSCC: false

opensearch:

# -- Deploy OpenSearch Helm chart and Configure Jira to use it as a search platform
#
enabled: false

credentials:
# -- Let the Helm chart create a secret with an auto generated initial admin password
#
createSecret: true

# -- Use an existing secret with the key OPENSEARCH_INITIAL_ADMIN_PASSWORD holding the initial admin password
#
existingSecretRef:
name:

# -- OpenSearch helm specific values, see: https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml
#
singleNode: true
resources:
requests:
cpu: 1
memory: 1Gi
persistence:
size: 10Gi
extraEnvs:
- name: plugins.security.ssl.http.enabled
value: "false"
envFrom:
- secretRef:
# -- If using a pre-created secret, make sure to change secret name to match opensearch.credentials.existingSecretRef.name
#
name: opensearch-initial-password
6 changes: 6 additions & 0 deletions src/test/config/jira/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@ database:
credentials:
secretName: jira-database-credentials

opensearch:
enabled: true
extraEnvs:
- name: plugins.security.disabled
value: "true"

monitoring:
exposeJmxMetrics: true
78 changes: 78 additions & 0 deletions src/test/java/test/JiraOpenSearchTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package test;

import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import test.helm.Helm;
import test.model.Product;

import java.util.Base64;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static test.jackson.JsonNodeAssert.assertThat;
import static test.model.Kind.Secret;

class JiraOpenSearchTest {

private static final Product JIRA = Product.jira;

private Helm helm;

@BeforeEach
void initHelm(TestInfo testInfo) {
helm = new Helm(testInfo);
}

@Test
void opensearch_statefulset_is_created_when_enabled() throws Exception {
final var resources = helm.captureKubeResourcesFromHelmChart(JIRA, Map.of(
"opensearch.enabled", "true"
));
final var statefulSet = resources.getStatefulSet("opensearch-cluster-master");
assertThat(statefulSet.getSpec()).isNotNull();
}

@Test
void opensearch_secret_contains_valid_base64_password() throws Exception {
final var resources = helm.captureKubeResourcesFromHelmChart(JIRA, Map.of(
"opensearch.enabled", "true"
));
final var secret = resources.get(Secret, "opensearch-initial-password");
JsonNode password = secret.getConfigMapData().path("OPENSEARCH_INITIAL_ADMIN_PASSWORD");
assertThat(password).isNotNull();
assertDoesNotThrow(() -> {
Base64.getDecoder().decode(password.asText());
}, "Password should be a valid Base64 encoded string");
byte[] decodedPassword = Base64.getDecoder().decode(password.asText());
assertEquals(40, decodedPassword.length, "The decoded password should have a length of 40 bytes.");
}

@Test
void opensearch_env_vars_are_set_with_default_credentials() throws Exception {
final var resources = helm.captureKubeResourcesFromHelmChart(JIRA, Map.of(
"opensearch.enabled", "true"
));

final var statefulSet = resources.getStatefulSet(JIRA.getHelmReleaseName());
final var env = statefulSet.getContainer().getEnv();
env.assertHasValue("ATL_SEARCH_PLATFORM", "opensearch");
env.assertHasValue("ATL_OPENSEARCH_HTTP_URL", "http://opensearch-cluster-master:9200");
env.assertHasValue("ATL_OPENSEARCH_USERNAME", "admin");
env.assertHasSecretRef("ATL_OPENSEARCH_PASSWORD", "opensearch-initial-password", "OPENSEARCH_INITIAL_ADMIN_PASSWORD");
}

@Test
void opensearch_env_vars_use_existing_secret_when_configured() throws Exception {
final var resources = helm.captureKubeResourcesFromHelmChart(JIRA, Map.of(
"opensearch.enabled", "true",
"opensearch.credentials.existingSecretRef.name", "my-opensearch-secret"
));

final var statefulSet = resources.getStatefulSet(JIRA.getHelmReleaseName());
final var env = statefulSet.getContainer().getEnv();
env.assertHasSecretRef("ATL_OPENSEARCH_PASSWORD", "my-opensearch-secret", "OPENSEARCH_INITIAL_ADMIN_PASSWORD");
}
}
11 changes: 8 additions & 3 deletions src/test/java/test/postinstall/OpenSearchInstallTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
@EnabledIf("isOSDeployed")
class OpenSearchInstallTest {
static boolean isOSDeployed() {
return productIs(Product.bitbucket);
return productIs(Product.bitbucket) || productIs(Product.jira);
}

private static KubeClient client;
Expand Down Expand Up @@ -64,8 +64,13 @@ void openSearchBeingUsed() {
// If this changes an alternative would be to use the fabric8 client ExecWatch/ExecListener to
// invoke curl from a pod.
final var indexURL = osIngressBase + "/_cat/indices?format=json";
when().get(indexURL).then()
.body("findAll { it.index == 'bitbucket-index-version' }[0]", hasEntry("docs.count", "1"));
if (productIs(Product.bitbucket)) {
when().get(indexURL).then()
.body("findAll { it.index == 'bitbucket-index-version' }[0]", hasEntry("docs.count", "1"));
} else if (productIs(Product.jira)) {
when().get(indexURL).then()
.body("findAll { it.index =~ /jira.*/ }.size()", greaterThan(0));
}
} catch (Exception e) {
retries--;
try {
Expand Down
19 changes: 19 additions & 0 deletions src/test/resources/expected_helm_output/jira/output.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,25 @@ data:
scrapeIntervalSeconds: 30
scrapeTimeoutSeconds: 20
nodeSelector: {}
opensearch:
credentials:
createSecret: true
existingSecretRef:
name: null
enabled: false
envFrom:
- secretRef:
name: opensearch-initial-password
extraEnvs:
- name: plugins.security.ssl.http.enabled
value: "false"
persistence:
size: 10Gi
resources:
requests:
cpu: 1
memory: 1Gi
singleNode: true
openshift:
runWithRestrictedSCC: false
ordinals:
Expand Down
2 changes: 1 addition & 1 deletion src/test/scripts/helm_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ package_functest_helm_chart() {
for ((NODE = 0; NODE < ${TARGET_REPLICA_COUNT:-0}; NODE += 1)); do
backdoor_services+="- ${PRODUCT_RELEASE_NAME}-${NODE}${NEWLINE}"
done
if [[ "$PRODUCT_NAME" == "bitbucket" ]]; then
if [[ "$PRODUCT_NAME" == "bitbucket" || "$PRODUCT_NAME" == "jira" ]]; then
echo "OpenSearch is being deployed, adding a backdoor"
backdoor_services+="- opensearch-cluster-master-0${NEWLINE}"
fi
Expand Down
Loading