diff --git a/.github/workflows/e2e-tf-deployment.yaml b/.github/workflows/e2e-tf-deployment.yaml
index e41e56b2c..974afd029 100644
--- a/.github/workflows/e2e-tf-deployment.yaml
+++ b/.github/workflows/e2e-tf-deployment.yaml
@@ -15,6 +15,17 @@ on:
pull_request_target:
types: [ labeled ]
workflow_dispatch:
+ inputs:
+ use_gateway_api:
+ description: 'Use Gateway API (Envoy Gateway) instead of NGINX Ingress'
+ required: false
+ type: boolean
+ default: false
+ terraform_branch:
+ description: 'Terraform repo branch to use (leave empty for latest LTS tag)'
+ required: false
+ type: string
+ default: ''
jobs:
test:
@@ -40,6 +51,7 @@ jobs:
TF_VAR_crowdstrike_kms_key_name: ${{ secrets.TF_VAR_CROWDSTRIKE_KMS_KEY_NAME }}
TF_VAR_crowdstrike_aws_account_id: ${{ secrets.TF_VAR_CROWDSTRIKE_AWS_ACCOUNT_ID }}
USE_DOMAIN: "true"
+ USE_GATEWAY_API: ${{ github.event.inputs.use_gateway_api || 'false' }}
steps:
- name: Checkout Helm charts
@@ -82,11 +94,17 @@ jobs:
- name: Checkout Deployment Automation
run: |
DEPLOYMENT_REPO='https://github.com/atlassian-labs/data-center-terraform.git'
- LTS=$(git ls-remote --tags --exit-code --refs "$DEPLOYMENT_REPO" \
- | sed -E 's/^[[:xdigit:]]+[[:space:]]+refs\/tags\/(.+)/\1/g' \
- | grep '^[0-9]*.[0-9]*.[0-9]*$' | sort -V | tail -1)
- echo "Using LTS version('${LTS}') of Deployment Automation to provision the infrastructure for Atlassian DC Products."
- git clone -b $LTS $DEPLOYMENT_REPO tf
+ CUSTOM_BRANCH="${{ github.event.inputs.terraform_branch }}"
+ if [ -n "$CUSTOM_BRANCH" ]; then
+ echo "Using custom branch '${CUSTOM_BRANCH}' of Deployment Automation."
+ git clone -b $CUSTOM_BRANCH $DEPLOYMENT_REPO tf
+ else
+ LTS=$(git ls-remote --tags --exit-code --refs "$DEPLOYMENT_REPO" \
+ | sed -E 's/^[[:xdigit:]]+[[:space:]]+refs\/tags\/(.+)/\1/g' \
+ | grep '^[0-9]*.[0-9]*.[0-9]*$' | sort -V | tail -1)
+ echo "Using LTS version('${LTS}') of Deployment Automation to provision the infrastructure for Atlassian DC Products."
+ git clone -b $LTS $DEPLOYMENT_REPO tf
+ fi
- name: Setup test environment
uses: actions/setup-go@v3
@@ -148,6 +166,14 @@ jobs:
crowd_install_local_chart = true
EOF
+ # Append Gateway API toggle if enabled
+ if [ "$USE_GATEWAY_API" = "true" ]; then
+ echo 'use_gateway_api = true' >> ./e2etest/test-config.tfvars.tmpl
+ echo "Gateway API mode enabled (Envoy Gateway will replace NGINX Ingress)"
+ else
+ echo "NGINX Ingress mode (default)"
+ fi
+
# Deploy infrastructure, install helm charts, run e2e tests, and cleanup all
# boto3 ignores AWS creds env vars for some reason
mkdir -p ~/.aws
diff --git a/.github/workflows/kind.yaml b/.github/workflows/kind.yaml
index d35268acd..6d13da72c 100644
--- a/.github/workflows/kind.yaml
+++ b/.github/workflows/kind.yaml
@@ -71,7 +71,7 @@ jobs:
- name: Verify ${{inputs.dc_app}} status
run: |
source src/test/scripts/kind/deploy_app.sh
- verify_ingress
+ verify_gateway_ingress
- name: Verify ${{inputs.dc_app}} grafana dashboards
run: |
@@ -90,6 +90,11 @@ jobs:
source src/test/scripts/kind/deploy_app.sh
verify_metrics
+ - name: Verify Gateway API integration
+ run: |
+ source src/test/scripts/kind/deploy_app.sh
+ verify_gateway
+
- name: Get debug info
if: always()
run: |
diff --git a/.github/workflows/openshift.yaml b/.github/workflows/openshift.yaml
index fb4751ef0..ac064c8dd 100644
--- a/.github/workflows/openshift.yaml
+++ b/.github/workflows/openshift.yaml
@@ -97,6 +97,98 @@ jobs:
run: |
oc apply -f src/test/config/openshift/shared-home-pvc.yaml
+ - name: Install Gateway API + Envoy Gateway
+ run: |
+ # Gateway API CRDs
+ oc apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
+ oc apply --server-side=true -f https://raw.githubusercontent.com/envoyproxy/gateway/v1.2.5/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+
+ oc wait --for condition=established --timeout=60s crd/gateways.gateway.networking.k8s.io
+ oc wait --for condition=established --timeout=60s crd/httproutes.gateway.networking.k8s.io
+ oc wait --for condition=established --timeout=60s crd/gatewayclasses.gateway.networking.k8s.io
+ oc wait --for condition=established --timeout=60s crd/envoyproxies.gateway.envoyproxy.io
+
+ # Envoy Gateway (installs into envoy-gateway-system)
+ # OpenShift admission can reject fixed runAsUser/seccomp settings unless the
+ # service accounts are allowed to use a more permissive SCC.
+ oc create namespace envoy-gateway-system --dry-run=client -o yaml | oc apply -f -
+ # Pre-grant SCCs to all service accounts in the namespace so Helm hooks can run
+ # even before the chart-created ServiceAccounts exist.
+ oc adm policy add-scc-to-group anyuid system:serviceaccounts:envoy-gateway-system || true
+ oc adm policy add-scc-to-group privileged system:serviceaccounts:envoy-gateway-system || true
+
+ helm install eg oci://docker.io/envoyproxy/gateway-helm \
+ --version v1.2.5 \
+ --namespace envoy-gateway-system \
+ --set deployment.envoyGateway.resources.requests.cpu=50m \
+ --set deployment.envoyGateway.resources.requests.memory=100Mi \
+ --skip-crds \
+ --timeout=600s \
+ --wait || { \
+ oc get all -n envoy-gateway-system || true; \
+ oc get events -n envoy-gateway-system --sort-by='.lastTimestamp' | tail -n 200 || true; \
+ exit 1; \
+ }
+
+ oc wait --for=condition=available deployment/envoy-gateway \
+ --namespace envoy-gateway-system \
+ --timeout=300s
+
+ # EnvoyProxy CR configures the proxy Service as ClusterIP (default for OpenShift; an OpenShift Route will handle external access).
+ oc apply -f src/test/config/openshift/envoy-proxy.yaml
+
+ # GatewayClass references the EnvoyProxy CR via parametersRef
+ cat << EOF | oc apply -f -
+ apiVersion: gateway.networking.k8s.io/v1
+ kind: GatewayClass
+ metadata:
+ name: eg
+ spec:
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parametersRef:
+ group: gateway.envoyproxy.io
+ kind: EnvoyProxy
+ name: openshift-proxy-config
+ namespace: envoy-gateway-system
+ EOF
+ oc wait --for=condition=Accepted gatewayclass/eg --timeout=180s || { oc get gatewayclass/eg -o yaml; oc get pods -n envoy-gateway-system -o wide || true; exit 1; }
+
+ # Create test Gateway
+ oc apply -f src/test/config/openshift/gateway.yaml
+ oc wait --for=condition=Accepted gateway/atlassian-gateway -n atlassian --timeout=300s || { oc describe gateway/atlassian-gateway -n atlassian; oc get events -n atlassian --sort-by='.lastTimestamp' | tail -n 200 || true; exit 1; }
+
+ # Wait for data-plane
+ oc wait --for=condition=Available deployment \
+ -n envoy-gateway-system \
+ -l gateway.envoyproxy.io/owning-gateway-name=atlassian-gateway \
+ --timeout=300s || { \
+ oc get deployments -n envoy-gateway-system -o wide; \
+ oc get pods -n envoy-gateway-system -o wide; \
+ oc describe deployment -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-name=atlassian-gateway || true; \
+ oc get events -n envoy-gateway-system --sort-by='.lastTimestamp' | tail -n 200 || true; \
+ exit 1; \
+ }
+
+ # Create an OpenShift Route so atlassian.apps.crc.testing reaches the
+ # Envoy proxy Service without port-forward or Host headers.
+ ENVOY_SVC=$(oc get svc -n envoy-gateway-system \
+ -l gateway.envoyproxy.io/owning-gateway-name=atlassian-gateway \
+ -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
+
+ if [ -z "${ENVOY_SVC}" ]; then
+ echo "[ERROR]: Envoy Gateway proxy service not found"
+ oc get svc -n envoy-gateway-system -o wide || true
+ exit 1
+ fi
+
+ oc -n envoy-gateway-system expose svc/${ENVOY_SVC} \
+ --name atlassian-gateway-proxy \
+ --hostname atlassian.apps.crc.testing || true
+
+ # Log the Route and Endpoint details for debugging
+ oc -n envoy-gateway-system get route atlassian-gateway-proxy -o yaml || true
+ oc -n envoy-gateway-system get endpoints ${ENVOY_SVC} -o yaml || true
+
- name: Deploy postgres database
run: |
source src/test/scripts/kind/deploy_app.sh
@@ -117,7 +209,7 @@ jobs:
run: |
export OPENSHIFT_VALUES="1"
source src/test/scripts/kind/deploy_app.sh
- verify_ingress
+ verify_gateway_ingress
- name: Verify ${{inputs.dc_app}} metrics availability
run: |
@@ -129,6 +221,11 @@ jobs:
source src/test/scripts/kind/deploy_app.sh
verify_openshift_analytics
+ - name: Verify Gateway API integration
+ run: |
+ source src/test/scripts/kind/deploy_app.sh
+ verify_gateway
+
- name: Get debug info
if: always()
run: |
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ad062d8a6..4ba51efd0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -25,6 +25,22 @@ For any pull requests, code owners should review the changes thoroughly and make
For external contributions, any Github action workflow related changes are not acceptable.
+## Windows Users
+
+This repository uses **symlinks** to share common Helm templates across product charts
+(see `src/main/charts/common_templates/`). Git on Windows doesn't enable symlinks by default — without them,
+Helm template rendering will fail.
+
+To enable symlinks on Windows:
+
+1. Enable **Developer Mode** in Windows Settings (Settings → Update & Security → For Developers), or run Git as Administrator
+2. Configure Git to create real symlinks:
+ ```
+ git config --global core.symlinks true
+ ```
+3. Re-clone the repository after changing this setting (existing clones won't retroactively fix symlinks)
+
+
### How to run E2E tests
The Data Center Helm Charts uses the latest release of [Deployment Automation for Atlassian DC on K8s](https://github.com/atlassian-labs/data-center-terraform#deployment-automation-for-atlassian-dc-on-k8s) for end-to-end testing. Internal reviewers can run the tests by adding `e2e` label on a pull request (for external contributions, be mindful of the changes as it will run on internal cloud environment).
\ No newline at end of file
diff --git a/docs/docs/examples/ingress/CONTROLLERS.md b/docs/docs/examples/ingress/CONTROLLERS.md
index 67ef8c8c9..700754c7c 100644
--- a/docs/docs/examples/ingress/CONTROLLERS.md
+++ b/docs/docs/examples/ingress/CONTROLLERS.md
@@ -1,6 +1,21 @@
-# Provisioning an Ingress controller
-In order for the provided Ingress resource to work, your Kubernetes cluster must have an ingress controller running. The Atlassian Helm charts have been tested with the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external}, however [alternatives can also be used](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/#additional-controllers){.external}.
+# Provisioning a traffic entry controller
-Here is an example of how these controllers can be installed and configured for use with the Atlassian Helm charts:
+To expose an Atlassian DC product outside your Kubernetes cluster, you must run **one** of the following:
-* [NGINX Ingress Controller](INGRESS_NGINX.md)
+- an **Ingress controller** (to process Kubernetes `Ingress` resources), or
+- a **Gateway API controller** (to process Kubernetes Gateway API resources, such as `HTTPRoute`).
+
+The Helm charts can render either:
+
+- a Kubernetes `Ingress` when `ingress.create: true`, or
+- a Kubernetes Gateway API `HTTPRoute` when `gateway.create: true`.
+
+These options are **mutually exclusive** (you cannot enable both `ingress.create` and `gateway.create`).
+
+!!!note "Sticky sessions are required"
+ Atlassian DC products require **session stickiness** ("sticky sessions") for high availability. With NGINX Ingress this is handled via controller annotations. With Gateway API you must configure stickiness using your chosen Gateway implementation.
+
+## Example guides
+
+- [NGINX Ingress Controller (Ingress)](INGRESS_NGINX.md)
+- [Gateway API controller (Gateway API)](GATEWAY_API.md)
diff --git a/docs/docs/examples/ingress/GATEWAY_API.md b/docs/docs/examples/ingress/GATEWAY_API.md
new file mode 100644
index 000000000..8cf10a5eb
--- /dev/null
+++ b/docs/docs/examples/ingress/GATEWAY_API.md
@@ -0,0 +1,99 @@
+# Gateway API controller (HTTPRoute)
+
+The Atlassian DC Helm charts support exposing products via the **Kubernetes Gateway API** by rendering a `HTTPRoute` resource when `gateway.create: true`.
+
+To use this, your cluster must have:
+
+- Gateway API CRDs installed
+- a Gateway API controller installed (for example, Envoy Gateway, Istio, etc.)
+- a `Gateway` resource that allows routes from your product namespace
+
+!!!note "What the charts create"
+ The charts create a **`HTTPRoute`** only. You must provision the **`GatewayClass`**, **`Gateway`**, and (optionally) **TLS certificates** in your cluster.
+
+## 1. Install a Gateway API controller
+
+Follow your chosen implementation's installation instructions:
+
+- Gateway API overview:
+- Implementations:
+
+## 2. Create a Gateway
+
+Create a `Gateway` that will accept `HTTPRoute` attachments from the namespace where you install the Atlassian product.
+
+The exact `gatewayClassName`, listener configuration, and TLS configuration depend on your chosen implementation.
+
+## 3. Configure the Helm chart
+
+Disable `ingress.create` and enable `gateway.create`. Provide a **parentRef** pointing to your `Gateway` and at least one **hostname**.
+
+```yaml
+ingress:
+ create: false
+
+gateway:
+ create: true
+ hostnames:
+ - confluence.example.com
+ https: true
+ parentRefs:
+ - name: atlassian-gateway
+ namespace: gateway-system # optional, defaults to release namespace
+ sectionName: https # optional, target a specific Gateway listener
+```
+
+!!!info "TLS termination"
+ With Gateway API, TLS termination is configured on the `Gateway` listeners (not on the `HTTPRoute`). The `gateway.https` value controls the product's proxy/URL settings (e.g., generating HTTPS links), but it does not provision certificates by itself.
+
+## Gateway values reference
+
+The `gateway` stanza is split into two groups:
+
+**Product configuration** (always active when `gateway.hostnames` is set):
+
+| Value | Description | Default |
+|-------|-------------|---------|
+| `gateway.create` | Create an `HTTPRoute` resource | `false` |
+| `gateway.hostnames` | Hostnames to route; first entry is used as the canonical hostname for base URL and proxy settings | `[]` |
+| `gateway.https` | Whether users access the application over HTTPS | `true` |
+| `gateway.externalPort` | Port users connect on; only set for non-standard ports | `443` (https) / `80` (http) |
+| `gateway.path` | Base path; falls back to `.service.contextPath` when empty | (empty) |
+
+**HTTPRoute configuration** (only applies when `gateway.create: true`):
+
+| Value | Description | Default |
+|-------|-------------|---------|
+| `gateway.parentRefs` | List of [ParentReference](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference){.external} objects (`name`, `namespace`, `sectionName`, etc.) | `[]` (required) |
+| `gateway.pathType` | Path matching type: `PathPrefix`, `Exact`, or `RegularExpression` | `PathPrefix` |
+| `gateway.annotations` | Annotations to add to the HTTPRoute | `{}` |
+| `gateway.labels` | Labels to add to the HTTPRoute | `{}` |
+| `gateway.filters` | [HTTPRouteFilter](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter){.external} list (header modification, redirects, URL rewrites) | `[]` |
+| `gateway.timeouts.request` | Total request timeout | `60s` |
+| `gateway.timeouts.backendRequest` | Backend request timeout | `60s` |
+| `gateway.additionalRules` | Extra [HTTPRouteRule](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule){.external} entries for advanced routing | `[]` |
+
+!!!note "Using gateway config without creating an HTTPRoute"
+ Setting `gateway.hostnames` activates gateway mode for the product's proxy and base-URL configuration **even when `gateway.create` is false**. This is useful when you have a pre-existing Gateway or external proxy/load balancer and only need the Helm chart to configure the product itself, without creating any Kubernetes routing resource.
+
+## 4. Timeouts
+
+The `gateway.timeouts` block replaces the Ingress-style `proxyReadTimeout` / `proxySendTimeout` settings:
+
+```yaml
+gateway:
+ timeouts:
+ request: "60s" # total request timeout
+ backendRequest: "60s" # backend request timeout
+```
+
+!!!warning "No Gateway API equivalents"
+ There is no standard Gateway API equivalent for `proxyConnectTimeout` or `maxBodySize`. If you need those, configure them through controller-specific policies (e.g. Envoy Gateway `BackendTrafficPolicy`).
+
+
+## Configure session affinity (sticky sessions)
+
+Session affinity is **required** for Atlassian DC products and is **not** part of the standard `HTTPRoute` API.
+
+See [Session affinity with Gateway API](GATEWAY_API_SESSION_AFFINITY.md) for implementation-specific examples (cookie-based) and links for further reading.
+
diff --git a/docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md b/docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md
new file mode 100644
index 000000000..0a3af5c05
--- /dev/null
+++ b/docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md
@@ -0,0 +1,70 @@
+# Session Affinity with Gateway API
+
+Atlassian DC products require **sticky sessions** so that a user is consistently routed to the same pod. With the NGINX Ingress controller this was handled automatically via annotations:
+
+```yaml
+nginx.ingress.kubernetes.io/affinity: "cookie"
+nginx.ingress.kubernetes.io/affinity-mode: "persistent"
+```
+
+With Gateway API, session affinity is **not** part of the standard `HTTPRoute` spec and must be configured separately.
+
+## Cookie naming
+
+Use a **dedicated routing cookie** (for example `ATLROUTE_`) rather than the application's own `JSESSIONID`. This avoids conflicts with the application's session cookie and matches the approach used by NGINX Ingress.
+
+## Options at a glance
+
+| Approach | Cookie-based | Standard channel |
+|----------|:---:|:---:|
+| Implementation policy (Option 1) | Yes | N/A (separate CRD) |
+| Experimental `sessionPersistence` (Option 2) | Yes | No (experimental) |
+
+---
+
+## Implementation-specific policies (recommended)
+
+Each Gateway API implementation provides its own policy resource for session affinity. Create the appropriate resource in the **same namespace** as your Helm release.
+
+### Envoy Gateway
+
+```yaml
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: -session-affinity
+ namespace:
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: -
+ loadBalancer:
+ type: ConsistentHash
+ consistentHash:
+ type: Cookie
+ cookie:
+ name: ATLROUTE_
+ ttl: 10h
+```
+
+**Explore further:**
+
+- Envoy Gateway load balancing & session persistence:
+- Envoy Gateway `BackendTrafficPolicy` API:
+- Istio consistent-hash via `DestinationRule`:
+
+---
+
+## Experimental `sessionPersistence` on HTTPRoute
+
+The Gateway API project has an **experimental** `sessionPersistence` field on HTTPRoute rules, tracked in **GEP-1619**. This field is **not** included in the standard-channel CRDs and will cause validation errors if those are installed.
+
+**Explore further:**
+
+- GEP-1619:
+- `SessionPersistence` field reference:
+- Experimental CRDs install (pick a release):
+
+!!!warning "Implementation support varies"
+ Even with experimental CRDs installed, not all Gateway implementations support `sessionPersistence`. Check your implementation's conformance/support documentation.
diff --git a/docs/docs/userguide/CONFIGURATION.md b/docs/docs/userguide/CONFIGURATION.md
index f18c06f3d..cd1628ac7 100644
--- a/docs/docs/userguide/CONFIGURATION.md
+++ b/docs/docs/userguide/CONFIGURATION.md
@@ -1,15 +1,22 @@
# Configuration
## :material-directions-fork: Ingress
-In order to make the Atlassian product available from outside of the Kubernetes cluster, a suitable HTTP/HTTPS ingress controller needs to be installed. The standard Kubernetes Ingress resource is not flexible enough for our needs, so a third-party ingress controller and resource definition must be provided. The exact details of the Ingress will be highly site-specific. These Helm charts were tested using the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external}. We also provide [example instructions](../examples/ingress/CONTROLLERS.md) on how this controller can be installed and configured.
+In order to make the Atlassian product available from outside of the Kubernetes cluster, you must provision a suitable HTTP/HTTPS traffic entry controller.
-The charts themselves provide a template for Ingress resource rules to be utilised by the provisioned controller. These include all required annotations and optional TLS configuration for the NGINX Ingress Controller.
+The Helm charts support exposing products using either:
+
+- Kubernetes **Ingress** resources (via an Ingress controller), or
+- Kubernetes **Gateway API** resources (via a Gateway API controller), by creating a `HTTPRoute`.
+
+The exact details will be highly site-specific. These Helm charts were tested using the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external}. We also provide [example instructions](../examples/ingress/CONTROLLERS.md) on how controllers can be installed and configured.
+
+The charts themselves provide templates for either `Ingress` or `HTTPRoute` resources (depending on configuration). These include the required knobs for configuring hostnames, paths, timeouts, and (for NGINX Ingress) annotations.
Some key considerations to note when configuring the controller are:
!!!Ingress requirements
* At a minimum, the ingress needs the ability to support long request timeouts, as well as session affinity (aka "sticky sessions").
- * The Ingress Resource provided as part of the Helm charts is geared toward the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external} and can be configured via the `ingress` stanza in the appropriate `values.yaml`. Some key aspects that can be configured include:
+ * The `Ingress` template provided as part of the Helm charts is geared toward the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external} and can be configured via the `ingress` stanza in the appropriate `values.yaml`. Some key aspects that can be configured include:
* Usage of the NGINX Ingress Controller
* Ingress Controller annotations
@@ -78,6 +85,41 @@ curl -I https://bitbucket.example.com/status
| Requests routing to different pods | Check if clustering is enabled |
| Session cookies not working | Ensure ingress controller supports session affinity |
+### Gateway API (HTTPRoute)
+
+The charts can create a Gateway API `HTTPRoute` instead of an `Ingress` resource.
+
+```yaml
+ingress:
+ create: false
+
+gateway:
+ create: true
+ hostnames:
+ - bitbucket.example.com
+ https: true
+ parentRefs:
+ - name: atlassian-gateway
+ namespace: gateway-system # optional
+```
+
+The `gateway` stanza supports additional options for fine-tuning the `HTTPRoute`:
+
+- **`parentRefs`** – list of [ParentReference](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference){.external} objects pointing to your `Gateway` resource (supports `name`, `namespace`, `sectionName`, etc.)
+- **`externalPort`** – non-standard port that users connect on (defaults to `443`/`80`)
+- **`timeouts`** – `request` and `backendRequest` timeouts (replaces Ingress `proxyReadTimeout`/`proxySendTimeout`)
+- **`filters`** – [HTTPRouteFilter](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter){.external} list for header modification, redirects, or URL rewrites
+- **`additionalRules`** – extra [HTTPRouteRule](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule){.external} entries for advanced routing (traffic splitting, header-based routing)
+- **`annotations`** / **`labels`** – metadata applied to the HTTPRoute resource
+
+!!!note "Gateway mode without creating an HTTPRoute"
+ Setting `gateway.hostnames` activates gateway mode for the product's proxy and base-URL configuration even when `gateway.create` is false. This allows use with a pre-existing Gateway or external proxy/load balancer.
+
+For the full list of values and usage examples, see the [Gateway API guide](../examples/ingress/GATEWAY_API.md).
+
+!!!important "Sticky sessions with Gateway API"
+ Session affinity is **not** part of the standard Gateway API `HTTPRoute` spec. You must configure it using your Gateway implementation (for example, Envoy Gateway policy resources or Istio traffic policy). See [Session affinity with Gateway API](../examples/ingress/GATEWAY_API_SESSION_AFFINITY.md) for working examples and fallbacks.
+
### Other Ingress Controllers
For other ingress controllers ([AWS ALB](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/){.external}, [Google Cloud Load Balancer](https://cloud.google.com/load-balancing){.external}, [Azure Application Gateway](https://azure.microsoft.com/en-us/products/application-gateway){.external}), refer to your controller's documentation for session stickiness configuration.
diff --git a/docs/docs/userguide/INSTALLATION.md b/docs/docs/userguide/INSTALLATION.md
index 4a33a754c..79a35e0ce 100644
--- a/docs/docs/userguide/INSTALLATION.md
+++ b/docs/docs/userguide/INSTALLATION.md
@@ -68,7 +68,12 @@ database:
Read about [Kubernetes secrets](https://kubernetes.io/docs/concepts/configuration/secret/){.external}.
## 4. Configure Ingress
-Using the `values.yaml` file obtained in [step 2](#2-obtain-valuesyaml), configure the [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/){.external} provisioned as part of the [Prerequisites](PREREQUISITES.md). The values you provide here will be used to provision an Ingress resource for the controller. Refer to the associated comments within the `values.yaml` file for additional details on how to configure the Ingress resource:
+Using the `values.yaml` file obtained in [step 2](#2-obtain-valuesyaml), configure how the product will be exposed outside the cluster. You can use either:
+
+- a Kubernetes **Ingress** (requires an [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/){.external}), or
+- a Kubernetes Gateway API **HTTPRoute** (requires a Gateway API controller and a `Gateway`).
+
+The values you provide here will be used to provision either an `Ingress` or an `HTTPRoute` resource for your chosen controller. Refer to the associated comments within the `values.yaml` file for additional details.
```yaml
ingress:
@@ -83,10 +88,29 @@ ingress:
tlsSecretName:
```
+Alternatively, to use Gateway API:
+
+```yaml
+ingress:
+ create: false
+
+gateway:
+ create: true # Creates an HTTPRoute resource
+ hostnames:
+ -
+ https: true
+ parentRefs:
+ - name:
+ namespace: # optional, defaults to release namespace
+```
+
!!!info "Ingress configuration"
For additional details on Ingress controllers see [the Ingress section of the configuration guide](CONFIGURATION.md#ingress).
- See an example of [how to set up a controller](../examples/ingress/CONTROLLERS.md).
+ See an example of [how to set up an NGINX Ingress Controller](../examples/ingress/INGRESS_NGINX.md) and an overview of [controller options](../examples/ingress/CONTROLLERS.md).
+
+!!!info "Gateway API configuration"
+ For Gateway API exposure, start with the [Gateway API controller guide](../examples/ingress/GATEWAY_API.md) and then configure [session affinity](../examples/ingress/GATEWAY_API_SESSION_AFFINITY.md). The Gateway API guide includes a full values reference and examples for timeouts, filters, and advanced routing.
## 5. Configure persistent storage
diff --git a/docs/docs/userguide/PREREQUISITES.md b/docs/docs/userguide/PREREQUISITES.md
index 34df1105b..a583e86cf 100644
--- a/docs/docs/userguide/PREREQUISITES.md
+++ b/docs/docs/userguide/PREREQUISITES.md
@@ -12,7 +12,7 @@ In order to deploy Atlassian’s Data Center products, the following is required
Before installing the Data Center Helm charts you need to set up your environment:
1. [Create and connect to the Kubernetes cluster](#create-and-connect-to-the-kubernetes-cluster)
-2. [Provision an Ingress Controller](#provision-an-ingress-controller)
+2. [Provision an Ingress or Gateway API controller](#provision-an-ingress-or-gateway-api-controller)
3. [Provision a database](#provision-a-database)
4. [Configure a shared-home volume](#configure-a-shared-home-volume)
5. [Configure a local-home volume](#configure-local-home-volume)
@@ -30,16 +30,22 @@ Before installing the Data Center Helm charts you need to set up your environmen
!!!example ""
See examples of [provisioning Kubernetes clusters on cloud-based providers](../examples/cluster/CLOUD_PROVIDERS.md).
-### :material-directions-fork: Provision an Ingress Controller
+
+### :material-directions-fork: Provision an Ingress or Gateway API controller
* This step is necessary in order to make your Atlassian product available from outside of the Kubernetes cluster after deployment.
-* The Kubernetes project supports and maintains ingress controllers for the major cloud providers including; [AWS](https://github.com/kubernetes-sigs/aws-load-balancer-controller#readme){.external}, [GCE](https://github.com/kubernetes/ingress-gce/blob/master/README.md#readme){.external} and [nginx](https://github.com/kubernetes/ingress-nginx/blob/master/README.md#readme){.external}. There are also a number of open-source [third-party projects available](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/){.external}.
-* Because different Kubernetes clusters use different ingress configurations/controllers, the Helm charts provide [Ingress Object](https://kubernetes.io/docs/concepts/services-networking/ingress/){.external} templates only.
-* The Ingress resource provided as part of the Helm charts is geared toward the [NGINX Ingress Controller](https://kubernetes.github.io/ingress-nginx/){.external} and can be configured via the `ingress` stanza in the appropriate `values.yaml` (an alternative controller can be used).
-* For more information about the Ingress controller go to the [Ingress section of the configuration guide](CONFIGURATION.md#ingress).
+* You can expose the product using either:
+ * the Kubernetes **Ingress** API (requires an Ingress controller), or
+ * the Kubernetes **Gateway API** (requires a Gateway API controller).
+* For Ingress, the Kubernetes project supports and maintains ingress controllers for the major cloud providers including; [AWS](https://github.com/kubernetes-sigs/aws-load-balancer-controller#readme){.external}, [GCE](https://github.com/kubernetes/ingress-gce/blob/master/README.md#readme){.external} and [nginx](https://github.com/kubernetes/ingress-nginx/blob/master/README.md#readme){.external}. There are also a number of open-source [third-party projects available](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/){.external}.
+* For Gateway API, see the Gateway API project docs and the list of implementations:
+ * Gateway API overview:
+ * Implementations:
+* The Helm charts can create either an `Ingress` (`ingress.create: true`) or an `HTTPRoute` (`gateway.create: true`) resource. These options are mutually exclusive.
+* For more information about exposure options and required configuration, see the [Ingress section of the configuration guide](CONFIGURATION.md#ingress).
!!!example ""
- See an example of [provisioning an NGINX Ingress Controller](../examples/ingress/CONTROLLERS.md).
+ See examples of [provisioning an NGINX Ingress Controller](../examples/ingress/INGRESS_NGINX.md) and [Gateway API setup](../examples/ingress/GATEWAY_API.md). For an overview, see [Provisioning a traffic entry controller](../examples/ingress/CONTROLLERS.md).
### :material-database: Provision a database
diff --git a/docs_internal/ROUTING.md b/docs_internal/ROUTING.md
new file mode 100644
index 000000000..cc1ea1815
--- /dev/null
+++ b/docs_internal/ROUTING.md
@@ -0,0 +1,212 @@
+# Routing: Ingress vs Gateway Internals
+
+This document explains how the `common.gateway.*` template helpers abstract over
+the `ingress` and `gateway` values sections, and why they are shaped this way.
+Read this when you need to understand the full picture.
+
+## The two values sections
+
+Users configure external access via one of two values sections:
+
+```yaml
+ingress: # K8s Ingress API path
+ host: ...
+ https: ...
+ path: ...
+
+gateway: # K8s Gateway API path (or external proxy)
+ hostnames: [ ... ]
+ https: ...
+ externalPort: ...
+ path: ...
+```
+
+Both sections carry two kinds of settings:
+
+1. **Product routing** — hostname, https, port, path. These tell the application
+ how users reach it (proxy settings, base URL, etc.). Present at the top of
+ each section.
+2. **Resource creation** — className, annotations, gatewayName, filters, etc.
+ These only matter when `create: true` and configure the Ingress/HTTPRoute
+ resource. Present below the section divider in `values.yaml`.
+
+The product routing settings are conceptually identical across both sections —
+only the key names differ (`host` vs `hostnames`, `port` vs `externalPort`).
+
+## Mode detection
+
+The helpers use two concepts to decide which values to read:
+
+| Helper | Returns `"true"` when | Purpose |
+|------------------|----------------------------------------------|----------------------------------------------------|
+| `useGatewayMode` | `gateway.hostnames` is non-empty | Determines which section to read values from |
+| `isConfigured` | `ingress.host` or `gateway.hostnames` is set | Determines if external access is configured at all |
+
+Key design decisions:
+
+- **Mode is implicit.** Setting `gateway.hostnames` activates gateway mode.
+ There is no explicit toggle — `gateway.create` controls HTTPRoute creation,
+ not mode selection.
+- **Mutual exclusion.** Setting both `ingress.host` and `gateway.hostnames`
+ is a validation error. The user must choose one path.
+- **`gateway.create` only controls the HTTPRoute resource.** A user with a
+ pre-existing Gateway or external load balancer can set `gateway.hostnames`
+ without `gateway.create: true` and get correct product configuration
+ (NOTE: only the first hostname will be used in such case).
+
+## Helper dependency graph
+
+```
+isConfigured ──────────────────── used by: statefulset guards, NOTES.txt, bamboo.baseUrl
+useGatewayMode ────┬───────────── used by: all helpers below
+ │
+ ├─ https ───── scheme
+ ├─ hostname │
+ ├─ externalPort│
+ │ ▼
+ │ origin ── used by: product baseUrl, NOTES.txt, SETUP_BASEURL
+ │
+ └─ path ──────────── used by: product path helpers (jira.path, etc.)
+```
+
+## How values flow into the product
+
+The helpers feed into two main outputs:
+
+### 1. Environment variables (statefulset.yaml)
+
+Guarded by `isConfigured` — only set when a hostname is configured.
+
+| Env var | Helper | Products |
+|----------------------------------------|-------------------|-----------|
+| `ATL_PROXY_NAME` / `SERVER_PROXY_NAME` | `hostname` | all |
+| `ATL_PROXY_PORT` / `SERVER_PROXY_PORT` | `externalPort` | all |
+| `ATL_TOMCAT_SCHEME` / `SERVER_SCHEME` | `scheme` | all |
+| `ATL_TOMCAT_SECURE` / `SERVER_SECURE` | `https` | all |
+| `ATL_BASE_URL` | `origin` + `path` | bamboo |
+| `SETUP_BASEURL` | `origin` | bitbucket |
+| `SERVER_CONTEXT_PATH` | `path` | bitbucket |
+
+### 2. Tomcat server.xml (configmap-server-config.yaml)
+
+Products that generate `server.xml` via Helm (jira, confluence, bamboo, crowd)
+set the `` proxy attributes using these helpers. The `proxyPort`
+attribute uses `externalPort` as a default but can be overridden per-product
+via `.tomcatConfig.proxyPort`.
+
+## Supported configurations
+
+### Standard: Ingress with resource creation
+
+```yaml
+ingress:
+ create: true
+ host: app.example.com
+ https: true
+```
+
+Creates an Ingress resource. Product configured via `ingress.*` values.
+
+### Standard: Gateway API with HTTPRoute creation
+
+```yaml
+gateway:
+ create: true
+ hostnames: [ app.example.com ]
+ https: true
+ gatewayName: my-gateway
+```
+
+Creates an HTTPRoute. Product configured via `gateway.*` values.
+
+### External proxy with gateway config (no resource created)
+
+```yaml
+gateway:
+ create: false
+ hostnames: [ app.example.com ]
+ https: true
+ externalPort: 8443
+```
+
+No K8s routing resource created. Product configured via `gateway.*` values.
+Use this when traffic is routed by an external load balancer, a pre-existing
+Gateway, or any proxy not managed by this chart.
+
+### External proxy with ingress config (legacy, no resource created)
+
+```yaml
+ingress:
+ create: false
+ host: app.example.com
+ https: true
+```
+
+Same as above but using `ingress.*` values. This is the legacy way to configure
+an external proxy.
+
+### Invalid: both configured
+
+```yaml
+ingress:
+ host: app.example.com # ← cannot set both
+gateway:
+ hostnames: [ app.example.com ] # ← cannot set both
+```
+
+Fails validation with: "Cannot set both gateway.hostnames and ingress.host".
+
+## Why "externalPort" instead of "port"?
+
+The `ingress.port` field existed as an undocumented, partially working feature.
+It was confusing for two reasons:
+
+1. **Unclear purpose.** "Port" of what? Users would reasonably assume it
+ configures the Ingress resource itself — but it doesn't. It never appeared
+ in the generated Ingress spec. It only fed into the product's proxy env vars
+ (`ATL_PROXY_PORT` / `SERVER_PROXY_PORT`).
+2. **Inconsistent support.** It worked for the proxy port env var but was
+ ignored by `bamboo.baseUrl` (so `ATL_BASE_URL` was wrong), ignored by all
+ NOTES.txt outputs (displayed URL omitted the port), and ignored by the
+ `configmap-server-config.yaml` `proxyPort` attribute (which had its own
+ separate default).
+
+The gateway section introduces `externalPort` to fix this:
+
+- **Clear name.** "External port" immediately communicates: this is the port
+ users hit to reach the application. Not an internal port, not a container
+ port, not a gateway listener port.
+- **Fully wired.** It flows consistently into env vars, `origin` (URL building),
+ NOTES.txt output, and Tomcat server.xml configuration.
+- **Documented.** It has a clear comment explaining that it does not change
+ the Gateway or load balancer — it must match the actual port in use.
+
+The `ingress.port` field is retained for backward compatibility but is not
+documented in the default `values.yaml`. New users should use the `gateway`
+section where `externalPort` provides a consistent, well-named alternative.
+
+## Why "common.gateway" naming?
+
+The helpers are namespaced under `common.gateway` even though they handle both
+ingress and gateway cases. This is a forward-looking choice — the Gateway API
+is the successor to the Ingress API in Kubernetes, and "gateway" as a general
+concept ("the entry point for traffic") fits both use cases. Renaming to
+something neutral like `common.routing` was considered but adds no clarity
+for the extra churn.
+
+## Product-specific helpers
+
+Each product has a `.path` helper that wraps `common.gateway.path`
+with the product's own `contextPath` default. The pattern is:
+
+```
+include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values..service.contextPath
+)
+```
+
+Bamboo additionally has `bamboo.baseUrl` which combines `origin` + `path`
+with a localhost fallback when no external access is configured.
diff --git a/src/main/charts/bamboo-agent/.helmignore b/src/main/charts/bamboo-agent/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/bamboo-agent/.helmignore
+++ b/src/main/charts/bamboo-agent/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/bamboo/.helmignore b/src/main/charts/bamboo/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/bamboo/.helmignore
+++ b/src/main/charts/bamboo/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/bamboo/Chart.yaml b/src/main/charts/bamboo/Chart.yaml
index a475a8f50..0decf82cc 100644
--- a/src/main/charts/bamboo/Chart.yaml
+++ b/src/main/charts/bamboo/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: bamboo
description: A chart for installing Bamboo Data Center on Kubernetes
type: application
-version: '2.0.9'
+version: '2.0.10'
appVersion: 12.1.3
kubeVersion: ">=1.21.x-0"
keywords:
@@ -20,7 +20,8 @@ deprecated: false
annotations:
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: |-
- - "Update appVersions for DC apps (#1074)"
+ - "Add Gateway API support via HTTPRoute resources"
+ - "Gateway API provides modern alternative to Ingress"
dependencies:
- name: common
version: 1.2.7
diff --git a/src/main/charts/bamboo/templates/NOTES.txt b/src/main/charts/bamboo/templates/NOTES.txt
index 4705f695d..b5c9524e5 100644
--- a/src/main/charts/bamboo/templates/NOTES.txt
+++ b/src/main/charts/bamboo/templates/NOTES.txt
@@ -13,8 +13,8 @@ To see the custom values you used for this release:
$ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }}
-{{ if .Values.ingress.create -}}
-{{ title .Chart.Name }} service URL: {{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ include "bamboo.ingressPath" . }}
+{{ if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{ title .Chart.Name }} service URL: {{ include "bamboo.baseUrl" . }}
{{- else }}
Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if contains "NodePort" .Values.bamboo.service.type }}
diff --git a/src/main/charts/bamboo/templates/_helpers.tpl b/src/main/charts/bamboo/templates/_helpers.tpl
index 33b9529ff..cbda467ff 100644
--- a/src/main/charts/bamboo/templates/_helpers.tpl
+++ b/src/main/charts/bamboo/templates/_helpers.tpl
@@ -62,35 +62,30 @@
Deduce the base URL for bamboo.
*/}}
{{- define "bamboo.baseUrl" -}}
- {{- if .Values.ingress.host -}}
- {{ ternary "https" "http" .Values.ingress.https -}}
- ://
- {{- if .Values.ingress.path -}}
- {{ .Values.ingress.host}}{{.Values.ingress.path }}
- {{- else -}}
- {{ .Values.ingress.host}}
- {{- end }}
- {{- else -}}
- {{- print "http://localhost:8085/" }}
- {{- end }}
+{{- if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{- include "common.gateway.origin" . -}}{{ include "bamboo.path" . -}}
+{{- else -}}
+{{- print "http://localhost:8085/" -}}
+{{- end -}}
{{- end }}
{{/*
-Create default value for ingress port
+Create default value for the service path.
*/}}
-{{- define "bamboo.ingressPort" -}}
-{{ default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
+{{- define "bamboo.path" -}}
+{{- include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values.bamboo.service.contextPath
+) -}}
{{- end }}
{{/*
-Create default value for ingress path
+Alias for backward compatibility with ingress templates.
*/}}
{{- define "bamboo.ingressPath" -}}
-{{- if .Values.ingress.path -}}
-{{- .Values.ingress.path -}}
-{{- else -}}
-{{ default ( "/" ) .Values.bamboo.service.contextPath -}}
-{{- end }}
+{{- include "bamboo.path" . -}}
{{- end }}
{{/*
@@ -447,3 +442,5 @@ Define additional hosts here to allow template overrides when used as a sub char
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 }}
+
+
diff --git a/src/main/charts/bamboo/templates/common_templates b/src/main/charts/bamboo/templates/common_templates
new file mode 120000
index 000000000..043f40ba4
--- /dev/null
+++ b/src/main/charts/bamboo/templates/common_templates
@@ -0,0 +1 @@
+../../common_templates
\ No newline at end of file
diff --git a/src/main/charts/bamboo/templates/configmap-server-config.yaml b/src/main/charts/bamboo/templates/configmap-server-config.yaml
index 2ddf89b13..68bd9ff4e 100644
--- a/src/main/charts/bamboo/templates/configmap-server-config.yaml
+++ b/src/main/charts/bamboo/templates/configmap-server-config.yaml
@@ -32,10 +32,10 @@ data:
protocol="{{ .Values.bamboo.tomcatConfig.protocol | default "HTTP/1.1" }}"
redirectPort="{{ .Values.bamboo.tomcatConfig.redirectPort | default "8443" }}"
acceptCount="{{ .Values.bamboo.tomcatConfig.acceptCount | default "100" }}"
- secure="{{ default (ternary "true" "false" .Values.ingress.https) .Values.bamboo.tomcatConfig.secure }}"
- scheme="{{ default (ternary "https" "http" .Values.ingress.https) .Values.bamboo.tomcatConfig.scheme }}"
- proxyName="{{ .Values.bamboo.tomcatConfig.proxyName | default .Values.ingress.host }}"
- proxyPort="{{ .Values.bamboo.tomcatConfig.proxyPort | default (ternary "443" "80" .Values.ingress.https) }}"
+ secure="{{ default (include "common.gateway.https" .) .Values.bamboo.tomcatConfig.secure }}"
+ scheme="{{ default (include "common.gateway.scheme" .) .Values.bamboo.tomcatConfig.scheme }}"
+ proxyName="{{ .Values.bamboo.tomcatConfig.proxyName | default (include "common.gateway.hostname" .) }}"
+ proxyPort="{{ .Values.bamboo.tomcatConfig.proxyPort | default (include "common.gateway.externalPort" .) }}"
{{- if .Values.bamboo.tomcatConfig.address }}
address="{{ .Values.bamboo.tomcatConfig.address }}"
diff --git a/src/main/charts/bamboo/templates/httproute.yaml b/src/main/charts/bamboo/templates/httproute.yaml
new file mode 100644
index 000000000..535544411
--- /dev/null
+++ b/src/main/charts/bamboo/templates/httproute.yaml
@@ -0,0 +1,50 @@
+{{- if .Values.gateway.create }}
+{{- include "common.gateway.validateConfig" . -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ include "common.names.fullname" . }}
+ labels:
+ {{- include "common.labels.commonLabels" . | nindent 4 }}
+ {{- with .Values.gateway.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.gateway.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ # Reference to the Gateway
+ parentRefs:
+ {{- toYaml .Values.gateway.parentRefs | nindent 2 }}
+
+ # Hostnames to match
+ hostnames:
+ {{- range .Values.gateway.hostnames }}
+ - {{ . | quote }}
+ {{- end }}
+
+ # Routing rules
+ rules:
+ # Default rule - routes to Bamboo service
+ - matches:
+ - path:
+ type: {{ .Values.gateway.pathType }}
+ value: {{ include "bamboo.path" . }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ {{- with .Values.gateway.filters }}
+ filters:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "common.names.fullname" . }}
+ port: {{ .Values.bamboo.service.port }}
+ weight: 100
+
+ {{- with .Values.gateway.additionalRules }}
+ {{- toYaml . | nindent 2 }}
+ {{- end }}
+{{- end }}
diff --git a/src/main/charts/bamboo/templates/statefulset.yaml b/src/main/charts/bamboo/templates/statefulset.yaml
index 856d7387b..c9dfcfc7b 100644
--- a/src/main/charts/bamboo/templates/statefulset.yaml
+++ b/src/main/charts/bamboo/templates/statefulset.yaml
@@ -1,3 +1,4 @@
+{{- include "common.gateway.validateConfig" . -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -117,7 +118,7 @@ spec:
image: {{ include "bamboo.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- {{ if .Values.ingress.https }}
+ {{ if eq (include "common.gateway.https" .) "true" }}
- name: ATL_TOMCAT_SCHEME
value: "https"
- name: ATL_TOMCAT_SECURE
@@ -131,11 +132,11 @@ spec:
{{ end }}
- name: ATL_TOMCAT_PORT
value: {{ .Values.bamboo.ports.http | quote }}
- {{ if .Values.ingress.host }}
+ {{ if eq (include "common.gateway.isConfigured" .) "true" }}
- name: ATL_PROXY_NAME
- value: {{ .Values.ingress.host | quote }}
+ value: {{ include "common.gateway.hostname" . | quote }}
- name: ATL_PROXY_PORT
- value: {{ include "bamboo.ingressPort" . | quote }}
+ value: {{ include "common.gateway.externalPort" . | quote }}
{{ end }}
{{- include "bamboo.databaseEnvVars" . | nindent 12 }}
- name: SET_PERMISSIONS
diff --git a/src/main/charts/bamboo/values.yaml b/src/main/charts/bamboo/values.yaml
index f10f7750f..f702f1a13 100644
--- a/src/main/charts/bamboo/values.yaml
+++ b/src/main/charts/bamboo/values.yaml
@@ -19,10 +19,11 @@
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#3-configure-database
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#5-configure-persistent-storage
#
-# To manage external access to the Bamboo instance, an ingress resource can also be configured
-# under the 'ingress' stanza. This requires a pre-provisioned ingress controller to be present.
+# To manage external access to the Bamboo instance, a Gateway API HTTPRoute or an Ingress
+# resource can be configured under the 'gateway' or 'ingress' stanza respectively.
+# This requires a pre-provisioned gateway/ingress controller to be present.
#
-# Additional details on pre-provisioning an ingress controller can be found here:
+# Additional details on configuring external access can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#4-configure-ingress
#
##
@@ -350,9 +351,9 @@ volumes:
# Ingress configuration
#
-# To make the Atlassian product available from outside of the K8s cluster an Ingress
-# Controller should be pre-provisioned. With this in place the configuration below
-# can be used to configure an appropriate Ingress Resource.
+# Use this section when routing external traffic to Bamboo via a Kubernetes Ingress
+# resource (K8s Ingress API). Requires a pre-provisioned Ingress Controller.
+# If using the Gateway API instead, see the 'gateway' section below.
# https://atlassian.github.io/data-center-helm-charts/userguide/CONFIGURATION/#ingress
#
ingress:
@@ -362,6 +363,27 @@ ingress:
#
create: false
+ # -- The fully-qualified hostname (FQDN) of the Bamboo instance. This value is used
+ # to configure the product's proxy settings and, when ingress.create is true,
+ # the Ingress resource routing rules.
+ #
+ host:
+
+ # -- Whether users access the application over HTTPS. Set to 'false' if not
+ # using TLS, e.g. when reaching the service via localhost port-forwarding.
+ #
+ https: true
+
+ # -- The base path for the application, e.g. '/bamboo'.
+ # Defaults to 'bamboo.service.contextPath'.
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when ingress.create is true.
+ # They configure the Ingress resource itself and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
# -- Set to true if you want to create an OpenShift Route instead of an Ingress
#
openShiftRoute: false
@@ -413,30 +435,12 @@ ingress:
#
proxySendTimeout: 60
- # -- The fully-qualified hostname (FQDN) of the Ingress Resource. Traffic coming in on
- # this hostname will be routed by the Ingress Resource to the appropriate backend
- # Service.
- #
- host:
-
- # -- The base path for the Ingress Resource. For example '/bamboo'. Based on a
- # 'ingress.host' value of 'company.k8s.com' this would result in a URL of
- # 'company.k8s.com/bamboo'. Default value is 'bamboo.service.contextPath'
- #
- path:
-
# -- The custom annotations that should be applied to the Ingress Resource.
# If using an ingress-nginx controller be sure that the annotations you add
# here are compatible with those already defined in the 'ingess.yaml' template
#
annotations: {}
- # -- Set to 'true' if browser communication with the application should be TLS
- # (HTTPS) enforced. If not using an ingress and you want to reach the service
- # on localhost using port-forwarding then this value should be set to 'false'
- #
- https: true
-
# -- The name of the K8s Secret that contains the TLS private key and corresponding
# certificate. When utilised, TLS termination occurs at the ingress point where
# traffic to the Service and it's Pods is in plaintext.
@@ -456,6 +460,121 @@ ingress:
# service: static-content-svc
# portNumber: 80
+# Gateway API configuration
+#
+# Use this section when routing external traffic to Bamboo via the Kubernetes
+# Gateway API, or when using an external proxy/load balancer without creating
+# any K8s routing resource. Only one of 'ingress' or 'gateway' should be used.
+# https://gateway-api.sigs.k8s.io/
+#
+gateway:
+
+ # -- Set to 'true' if an HTTPRoute Resource should be created. This depends on a
+ # pre-provisioned Gateway API controller being available and a Gateway resource.
+ # Cannot be enabled if ingress.create is true.
+ #
+ create: false
+
+ # -- The hostnames that should be routed to Bamboo. At least one hostname
+ # is required when gateway.create is true. Setting hostnames activates gateway
+ # mode for product configuration even when gateway.create is false, allowing
+ # use with a pre-existing Gateway or external proxy.
+ # The first entry is used as the canonical hostname for base URL, proxy
+ # settings, and NOTES output — list the primary/public hostname first.
+ #
+ hostnames: []
+ # - bamboo.example.com
+ # - ci.example.com
+
+ # -- Whether users access the application over HTTPS.
+ # This does not configure TLS on the Gateway or load balancer — it must match
+ # how traffic is actually routed to the application.
+ #
+ https: true
+
+ # -- The port users connect on. Defaults to 443 (https) or 80 (http).
+ # This does not change the Gateway or load balancer port — it must match
+ # the port that is actually used. Only set for non-standard ports.
+ #
+ # externalPort:
+
+ # -- The base path for routing. When empty, falls back to the product's
+ # service.contextPath (same behavior as ingress). Set explicitly to
+ # override, e.g. "/bamboo".
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when gateway.create is true.
+ # They configure the HTTPRoute resource and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
+ # -- Reference to the parent Gateway resource. Supports any standard
+ # parentRef fields (name, namespace, sectionName, etc.).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ #
+ parentRefs: []
+ # - name: example-gateway
+ # namespace: example-gateway-namespace
+ # sectionName: https
+
+ # -- Path matching type. Can be "PathPrefix", "Exact", or "RegularExpression".
+ # PathPrefix is recommended for most use cases.
+ #
+ pathType: "PathPrefix"
+
+ # -- Annotations to add to the HTTPRoute resource.
+ #
+ annotations: {}
+ # kubernetes.io/ingress.class: gateway
+ # cert-manager.io/cluster-issuer: letsencrypt
+
+ # -- Labels to add to the HTTPRoute resource.
+ #
+ labels: {}
+ # environment: production
+ # team: platform
+
+ # -- HTTP filters to apply to requests. Can be used to add/remove headers,
+ # perform redirects, or rewrite URLs.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter
+ #
+ filters: []
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # add:
+ # - name: X-Forwarded-Proto
+ # value: https
+
+ # -- Advanced routing rules. Use this for complex routing scenarios like
+ # header-based routing, traffic splitting, or multiple backends.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ #
+ additionalRules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api
+ # backendRefs:
+ # - name: bamboo-api
+ # port: 8085
+
+ # -- Session affinity (sticky sessions) is required for Atlassian DC products but
+ # is not part of the standard Gateway API HTTPRoute spec. It must be configured
+ # separately through your Gateway implementation's own policy resources.
+ # See docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md for working examples.
+
+ # -- Timeout configuration for HTTPRoute rules.
+ # Note: when migrating from Ingress, these replace proxyReadTimeout and
+ # proxySendTimeout. There is no Gateway API equivalent for
+ # proxyConnectTimeout or maxBodySize — those require controller-specific
+ # policies (e.g. Envoy Gateway BackendTrafficPolicy).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteTimeouts
+ #
+ timeouts:
+ request: "60s"
+ backendRequest: "60s"
+
# Bamboo configuration
#
bamboo:
@@ -880,13 +999,13 @@ bamboo:
protocol: "HTTP/1.1"
redirectPort: "8443"
acceptCount: "100"
- # secure is retrieved from ingress.https value
+ # secure is set based on the https setting (gateway.https or ingress.https)
secure:
- # scheme is set depending on ingress.https value (http if false, https if true)
+ # scheme is set based on the https setting (http if false, https if true)
scheme:
- # proxyName is retrieved from ingress.host value
+ # proxyName is set to the configured hostname (gateway.hostnames[0] or ingress.host)
proxyName:
- # proxyPort is set depending on ingress.https value (80 if http, 443 if https)
+ # proxyPort is set to the external port (defaults to 443 for https, 80 for http)
proxyPort:
maxHttpHeaderSize: "8192"
address:
diff --git a/src/main/charts/bitbucket/.helmignore b/src/main/charts/bitbucket/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/bitbucket/.helmignore
+++ b/src/main/charts/bitbucket/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/bitbucket/Chart.yaml b/src/main/charts/bitbucket/Chart.yaml
index 5d669f841..a8e106a47 100644
--- a/src/main/charts/bitbucket/Chart.yaml
+++ b/src/main/charts/bitbucket/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: bitbucket
description: A chart for installing Bitbucket Data Center on Kubernetes
type: application
-version: '2.0.9'
+version: '2.0.10'
appVersion: 10.2.1
kubeVersion: ">=1.21.x-0"
keywords:
@@ -20,8 +20,8 @@ deprecated: false
annotations:
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: |-
- - "fix nodeport svc (#1076)"
- - "Update appVersions for DC apps (#1075)"
+ - "Add Gateway API support via HTTPRoute resources"
+ - "Gateway API provides modern alternative to Ingress"
dependencies:
- name: common
diff --git a/src/main/charts/bitbucket/templates/NOTES.txt b/src/main/charts/bitbucket/templates/NOTES.txt
index 1ccebd353..9501e53c5 100644
--- a/src/main/charts/bitbucket/templates/NOTES.txt
+++ b/src/main/charts/bitbucket/templates/NOTES.txt
@@ -13,8 +13,8 @@ To see the custom values you used for this release:
$ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }}
-{{ if .Values.ingress.create -}}
-{{ title .Chart.Name }} service URL: {{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ include "bitbucket.ingressPath" . }}
+{{ if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{ title .Chart.Name }} service URL: {{ include "common.gateway.origin" . }}{{ include "bitbucket.path" . }}
{{- else }}
Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if contains "NodePort" .Values.bitbucket.service.type }}
@@ -64,7 +64,7 @@ Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if and (.Values.bitbucket.mesh.enabled) (not .Values.bitbucket.mesh.nodeAutoRegistration)}}
-Bitbucket Mesh deployed. You can register Mesh nodes at {{ if .Values.ingress.create }}{{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ include "bitbucket.ingressPath" . }}{{ else }}${BITBUCKET_URL}{{ end }}/admin/git/mesh using the following URLs:
+Bitbucket Mesh deployed. You can register Mesh nodes at {{ if eq (include "common.gateway.isConfigured" .) "true" }}{{ include "common.gateway.origin" . }}{{ include "bitbucket.path" . }}{{ else }}${BITBUCKET_URL}{{ end }}/admin/git/mesh using the following URLs:
{{- range $index := until (.Values.bitbucket.mesh.replicaCount | int) }}
{{- with $ }}
diff --git a/src/main/charts/bitbucket/templates/_helpers.tpl b/src/main/charts/bitbucket/templates/_helpers.tpl
index da95d3983..87700b1bb 100644
--- a/src/main/charts/bitbucket/templates/_helpers.tpl
+++ b/src/main/charts/bitbucket/templates/_helpers.tpl
@@ -125,26 +125,23 @@ Mesh Pod labels
{{- end }}
{{- end }}
-{{- define "bitbucket.baseUrl" -}}
-{{ ternary "https" "http" .Values.ingress.https -}}
-://
-{{- .Values.ingress.host -}}
-{{ with .Values.ingress.port }}:{{ . }}{{ end }}
+{{/*
+Create default value for the service path.
+*/}}
+{{- define "bitbucket.path" -}}
+{{- include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values.bitbucket.service.contextPath
+) -}}
{{- end }}
{{/*
-Create default value for ingress path
+Alias for backward compatibility with ingress templates.
*/}}
{{- define "bitbucket.ingressPath" -}}
-{{- if .Values.ingress.path -}}
-{{- .Values.ingress.path -}}
-{{- else -}}
-{{ default ( "/" ) .Values.bitbucket.service.contextPath -}}
-{{- end }}
-{{- end }}
-
-{{- define "bitbucket.ingressPort" -}}
-{{ default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
+{{- include "bitbucket.path" . -}}
{{- end }}
{{/*
@@ -608,3 +605,5 @@ set -e; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts; chmod 664 /var/ssl/
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 }}
+
+
diff --git a/src/main/charts/bitbucket/templates/common_templates b/src/main/charts/bitbucket/templates/common_templates
new file mode 120000
index 000000000..043f40ba4
--- /dev/null
+++ b/src/main/charts/bitbucket/templates/common_templates
@@ -0,0 +1 @@
+../../common_templates
\ No newline at end of file
diff --git a/src/main/charts/bitbucket/templates/httproute.yaml b/src/main/charts/bitbucket/templates/httproute.yaml
new file mode 100644
index 000000000..af8377f0f
--- /dev/null
+++ b/src/main/charts/bitbucket/templates/httproute.yaml
@@ -0,0 +1,50 @@
+{{- if .Values.gateway.create }}
+{{- include "common.gateway.validateConfig" . -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ include "common.names.fullname" . }}
+ labels:
+ {{- include "common.labels.commonLabels" . | nindent 4 }}
+ {{- with .Values.gateway.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.gateway.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ # Reference to the Gateway
+ parentRefs:
+ {{- toYaml .Values.gateway.parentRefs | nindent 2 }}
+
+ # Hostnames to match
+ hostnames:
+ {{- range .Values.gateway.hostnames }}
+ - {{ . | quote }}
+ {{- end }}
+
+ # Routing rules
+ rules:
+ # Default rule - routes to Bitbucket service
+ - matches:
+ - path:
+ type: {{ .Values.gateway.pathType }}
+ value: {{ include "bitbucket.path" . }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ {{- with .Values.gateway.filters }}
+ filters:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "common.names.fullname" . }}
+ port: {{ .Values.bitbucket.service.port }}
+ weight: 100
+
+ {{- with .Values.gateway.additionalRules }}
+ {{- toYaml . | nindent 2 }}
+ {{- end }}
+{{- end }}
diff --git a/src/main/charts/bitbucket/templates/statefulset.yaml b/src/main/charts/bitbucket/templates/statefulset.yaml
index a98179c44..6a6033b46 100644
--- a/src/main/charts/bitbucket/templates/statefulset.yaml
+++ b/src/main/charts/bitbucket/templates/statefulset.yaml
@@ -1,6 +1,7 @@
{{/* This describes a k8s StatefulSet for deploying Bitbucket for testing */}}
{{/* the default can be removed in v2.0.0 */}}
{{- $mirror := default dict .Values.bitbucket.mirror }}
+{{- include "common.gateway.validateConfig" . -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -238,13 +239,13 @@ spec:
{{- end }}
- name: PLUGIN_SSH_PORT
value: {{ .Values.bitbucket.ports.ssh | quote }}
- {{ if .Values.ingress.host }}
+ {{ if eq (include "common.gateway.isConfigured" .) "true" }}
- name: SERVER_PROXY_NAME
- value: {{ .Values.ingress.host | quote }}
+ value: {{ include "common.gateway.hostname" . | quote }}
- name: SERVER_PROXY_PORT
- value: {{ include "bitbucket.ingressPort" . | quote }}
+ value: {{ include "common.gateway.externalPort" . | quote }}
- name: SETUP_BASEURL
- value: {{ include "bitbucket.baseUrl" . | quote }}
+ value: {{ include "common.gateway.origin" . | quote }}
{{- with .Values.bitbucket.displayName }}
- name: SETUP_DISPLAYNAME
value: {{ . | quote }}
@@ -255,10 +256,10 @@ spec:
{{- end }}
{{ end }}
- name: SERVER_CONTEXT_PATH
- value: {{ include "bitbucket.ingressPath" . | quote }}
+ value: {{ include "bitbucket.path" . | quote }}
- name: SERVER_PORT
value: {{ .Values.bitbucket.ports.http | quote }}
- {{ if .Values.ingress.https }}
+ {{ if eq (include "common.gateway.https" .) "true" }}
- name: SERVER_SCHEME
value: "https"
- name: SERVER_SECURE
diff --git a/src/main/charts/bitbucket/values.yaml b/src/main/charts/bitbucket/values.yaml
index 5813504f9..cb795e529 100644
--- a/src/main/charts/bitbucket/values.yaml
+++ b/src/main/charts/bitbucket/values.yaml
@@ -11,10 +11,11 @@
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#3-configure-database
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#5-configure-persistent-storage
#
-# To manage external access to the Bitbucket instance, an ingress resource can also be configured
-# under the 'ingress' stanza. This requires a pre-provisioned ingress controller to be present.
+# To manage external access to the Bitbucket instance, a Gateway API HTTPRoute or an Ingress
+# resource can be configured under the 'gateway' or 'ingress' stanza respectively.
+# This requires a pre-provisioned gateway/ingress controller to be present.
#
-# Additional details on pre-provisioning an ingress controller can be found here:
+# Additional details on configuring external access can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#4-configure-ingress
#
# Unlike the other products, Bitbucket has the added advantage that it can be fully
@@ -426,9 +427,9 @@ volumes:
# Ingress configuration
#
-# To make the Atlassian product available from outside the K8s cluster an Ingress
-# Controller should be pre-provisioned. With this in place the configuration below
-# can be used to configure an appropriate Ingress Resource.
+# Use this section when routing external traffic to Bitbucket via a Kubernetes Ingress
+# resource (K8s Ingress API). Requires a pre-provisioned Ingress Controller.
+# If using the Gateway API instead, see the 'gateway' section below.
# https://atlassian.github.io/data-center-helm-charts/userguide/CONFIGURATION/#ingress
#
ingress:
@@ -438,6 +439,27 @@ ingress:
#
create: false
+ # -- The fully-qualified hostname (FQDN) of the Bitbucket instance. This value is used
+ # to configure the product's proxy settings and, when ingress.create is true,
+ # the Ingress resource routing rules.
+ #
+ host:
+
+ # -- Whether users access the application over HTTPS. Set to 'false' if not
+ # using TLS, e.g. when reaching the service via localhost port-forwarding.
+ #
+ https: true
+
+ # -- The base path for the application, e.g. '/bitbucket'.
+ # Defaults to 'bitbucket.service.contextPath'.
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when ingress.create is true.
+ # They configure the Ingress resource itself and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
# -- Set to true if you want to create an OpenShift Route instead of an Ingress
#
openShiftRoute: false
@@ -489,29 +511,12 @@ ingress:
#
proxySendTimeout: 60
- # -- The fully-qualified hostname (FQDN) of the Ingress Resource. Traffic coming in on
- # this hostname will be routed by the Ingress Resource to the appropriate backend
- # Service.
- #
- host:
-
- # -- The base path for the Ingress Resource. For example '/bitbucket'. Based on a
- # 'ingress.host' value of 'company.k8s.com' this would result in a URL of
- # 'company.k8s.com/bitbucket'. Default value is 'bitbucket.service.contextPath'.
- #
- path:
-
# -- The custom annotations that should be applied to the Ingress Resource.
# If using an ingress-nginx controller be sure that the annotations you add
# here are compatible with those already defined in the 'ingess.yaml' template
#
annotations: {}
- # -- Set to 'true' if browser communication with the application should be TLS
- # (HTTPS) enforced.
- #
- https: true
-
# -- The name of the K8s Secret that contains the TLS private key and corresponding
# certificate. When utilised, TLS termination occurs at the ingress point where
# traffic to the Service, and it's Pods is in plaintext.
@@ -531,6 +536,122 @@ ingress:
# service: static-content-svc
# portNumber: 80
+# Gateway API configuration
+#
+# Use this section when routing external traffic to Bitbucket via the Kubernetes
+# Gateway API, or when using an external proxy/load balancer without creating
+# any K8s routing resource. Only one of 'ingress' or 'gateway' should be used.
+# https://gateway-api.sigs.k8s.io/
+#
+gateway:
+
+ # -- Set to 'true' if an HTTPRoute Resource should be created. This depends on a
+ # pre-provisioned Gateway API controller being available and a Gateway resource.
+ # Cannot be enabled if ingress.create is true.
+ #
+ create: false
+
+ # -- The hostnames that should be routed to Bitbucket. At least one hostname
+ # is required when gateway.create is true. Setting hostnames activates gateway
+ # mode for product configuration even when gateway.create is false, allowing
+ # use with a pre-existing Gateway or external proxy.
+ # The first entry is used as the canonical hostname for base URL, proxy
+ # settings, and NOTES output — list the primary/public hostname first.
+ #
+ hostnames: []
+ # - bitbucket.example.com
+ # - bb.example.com
+
+ # -- Whether users access the application over HTTPS.
+ # This does not configure TLS on the Gateway or load balancer — it must match
+ # how traffic is actually routed to the application.
+ #
+ https: true
+
+ # -- The port users connect on. Defaults to 443 (https) or 80 (http).
+ # This does not change the Gateway or load balancer port — it must match
+ # the port that is actually used. Only set for non-standard ports.
+ #
+ # externalPort:
+
+ # -- The base path for routing. When empty, falls back to the product's
+ # service.contextPath (same behavior as ingress). Set explicitly to
+ # override, e.g. "/bitbucket".
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when gateway.create is true.
+ # They configure the HTTPRoute resource and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
+ # -- Reference to the parent Gateway resource. Supports any standard
+ # parentRef fields (name, namespace, sectionName, etc.).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ #
+ parentRefs: []
+ # - name: example-gateway
+ # namespace: example-gateway-namespace
+ # sectionName: https
+
+ # -- Path matching type. Can be "PathPrefix", "Exact", or "RegularExpression".
+ # PathPrefix is recommended for most use cases.
+ #
+ pathType: "PathPrefix"
+
+ # -- Annotations to add to the HTTPRoute resource.
+ #
+ annotations: {}
+ # kubernetes.io/ingress.class: gateway
+ # cert-manager.io/cluster-issuer: letsencrypt
+
+ # -- Labels to add to the HTTPRoute resource.
+ #
+ labels: {}
+ # environment: production
+ # team: platform
+
+ # -- HTTP filters to apply to requests. Can be used to add/remove headers,
+ # perform redirects, or rewrite URLs.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter
+ #
+ filters: []
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # add:
+ # - name: X-Forwarded-Proto
+ # value: https
+
+ # -- Advanced routing rules. Use this for complex routing scenarios like
+ # header-based routing, traffic splitting, or multiple backends.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ #
+ additionalRules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api
+ # backendRefs:
+ # - name: bitbucket-api-v2
+ # port: 8080
+ # weight: 100
+
+ # -- Session affinity (sticky sessions) is required for Atlassian DC products but
+ # is not part of the standard Gateway API HTTPRoute spec. It must be configured
+ # separately through your Gateway implementation's own policy resources.
+ # See docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md for working examples.
+
+ # -- Timeout configuration for HTTPRoute rules.
+ # Note: when migrating from Ingress, these replace proxyReadTimeout and
+ # proxySendTimeout. There is no Gateway API equivalent for
+ # proxyConnectTimeout or maxBodySize — those require controller-specific
+ # policies (e.g. Envoy Gateway BackendTrafficPolicy).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteTimeouts
+ #
+ timeouts:
+ request: "60s"
+ backendRequest: "60s"
+
# Bitbucket configuration
#
bitbucket:
diff --git a/src/main/charts/common_templates/README.md b/src/main/charts/common_templates/README.md
new file mode 100644
index 000000000..922b82b53
--- /dev/null
+++ b/src/main/charts/common_templates/README.md
@@ -0,0 +1,48 @@
+# Common Templates
+
+Shared Helm template helpers that are symlinked into each product chart's `templates/` directory.
+
+## Why this pattern?
+
+The existing `common/` library chart is published as a remote Helm dependency. Any change to it
+requires a **two-step rollout**: publish the library first, then update each product chart's
+`Chart.yaml` to reference the new version.
+
+This `common_templates/` directory avoids that by using **symlinks**. Each product chart has a
+symlink at `templates/common_templates → ../../common_templates`, so Helm picks up these templates
+directly. Changes here take effect immediately across all products — no publishing, no version
+bumps, single PR.
+
+## When to use this vs `common/`
+
+**All new shared templates should go in `common_templates/` (this directory).**
+
+The `common/` library chart is a legacy pattern that requires a two-step release process for
+every change. It should not be used for new shared templates. Existing templates in `common/`
+(`_labels.tpl`, `_names.tpl`, `_jmx.tpl`) should be incrementally migrated here when they are
+next modified — there is no technical reason to keep them in the library chart.
+
+## How it works
+
+Each product chart has a symlink:
+
+```
+src/main/charts//templates/common_templates → ../../common_templates
+```
+
+Helm follows symlinks during `helm template` and `helm package`, so:
+
+- **Local development**: templates are resolved via the symlink
+- **Published charts**: `helm package` embeds the actual file content in the `.tgz` — consumers see regular files, not
+ symlinks
+
+## Adding a new shared template
+
+1. Create your `.tpl` file in this directory
+2. That's it — all product charts pick it up automatically via the existing symlink
+
+## Platform note
+
+Git on Windows doesn't enable symlinks by default. Contributors on Windows need
+`git config core.symlinks true` (or run Git as admin) for symlinks to work correctly.
+
diff --git a/src/main/charts/common_templates/_gateway.tpl b/src/main/charts/common_templates/_gateway.tpl
new file mode 100644
index 000000000..bb0cc9fdf
--- /dev/null
+++ b/src/main/charts/common_templates/_gateway.tpl
@@ -0,0 +1,112 @@
+{{/* vim: set filetype=mustache: */}}
+
+{{/*
+Returns "true" if external access is configured via either ingress or gateway.
+True when ingress.host is set or gateway.hostnames is non-empty.
+*/}}
+{{- define "common.gateway.isConfigured" -}}
+ {{- ternary "true" "false" (or (not (empty .Values.ingress.host)) (not (empty .Values.gateway.hostnames))) -}}
+{{- end -}}
+
+{{/*
+Returns "true" if gateway mode should be used (vs ingress mode) for product setup.
+True when gateway.hostnames is non-empty, regardless of whether an HTTPRoute is created.
+This allows using gateway config with a pre-existing Gateway/proxy without gateway.create.
+*/}}
+{{- define "common.gateway.useGatewayMode" -}}
+ {{- ternary "true" "false" (not (empty .Values.gateway.hostnames)) -}}
+{{- end -}}
+
+{{/*
+Validates gateway/ingress configuration.
+Ensures mutual exclusion and required fields.
+*/}}
+{{- define "common.gateway.validateConfig" -}}
+ {{- if and .Values.gateway.create .Values.ingress.create -}}
+ {{- fail "ERROR: Cannot enable both gateway.create and ingress.create" -}}
+ {{- end -}}
+ {{- if and (not (empty .Values.gateway.hostnames)) (not (empty .Values.ingress.host)) -}}
+ {{- fail "ERROR: Cannot set both gateway.hostnames and ingress.host — use one or the other" -}}
+ {{- end -}}
+ {{- if and .Values.gateway.create (empty .Values.gateway.parentRefs) -}}
+ {{- fail "ERROR: gateway.parentRefs is required when gateway.create is true" -}}
+ {{- end -}}
+ {{- if and .Values.gateway.create (not (empty .Values.gateway.parentRefs)) (not (index .Values.gateway.parentRefs 0).name) -}}
+ {{- fail "ERROR: gateway.parentRefs[0].name is required when gateway.create is true" -}}
+ {{- end -}}
+ {{- if and .Values.gateway.create (not .Values.gateway.hostnames) -}}
+ {{- fail "ERROR: gateway.hostnames must contain at least one hostname when gateway.create is true" -}}
+ {{- end -}}
+{{- end -}}
+
+{{/*
+Returns "true" or "false" string for whether HTTPS is enabled.
+Uses gateway.https if gateway config is active, otherwise ingress.https.
+*/}}
+{{- define "common.gateway.https" -}}
+ {{- $useGateway := eq (include "common.gateway.useGatewayMode" .) "true" -}}
+ {{- ternary .Values.gateway.https .Values.ingress.https $useGateway -}}
+{{- end -}}
+
+{{/*
+Returns "https" or "http" based on the current ingress/gateway HTTPS setting.
+*/}}
+{{- define "common.gateway.scheme" -}}
+ {{- ternary "https" "http" (eq (include "common.gateway.https" .) "true") -}}
+{{- end -}}
+
+{{/*
+Returns the canonical hostname for the service.
+Uses first gateway hostname if gateway config is active, otherwise ingress.host.
+*/}}
+{{- define "common.gateway.hostname" -}}
+ {{- if eq (include "common.gateway.useGatewayMode" .) "true" -}}
+ {{- index .Values.gateway.hostnames 0 -}}
+ {{- else -}}
+ {{- .Values.ingress.host -}}
+ {{- end -}}
+{{- end -}}
+
+{{/*
+Returns the external port the application is accessed on.
+Defaults to "443" (https) or "80" (http).
+*/}}
+{{- define "common.gateway.externalPort" -}}
+ {{- if eq (include "common.gateway.useGatewayMode" .) "true" -}}
+ {{- default (ternary "443" "80" .Values.gateway.https) .Values.gateway.externalPort -}}
+ {{- else -}}
+ {{- default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
+ {{- end -}}
+{{- end -}}
+
+{{/*
+Returns the service path. Handles gateway vs ingress mode with a contextPath fallback.
+Usage:
+include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values..service.contextPath
+)
+*/}}
+{{- define "common.gateway.path" -}}
+{{- $explicitPath := ternary .gatewayPath .ingressPath (eq .useGatewayMode "true") -}}
+{{- if $explicitPath -}}
+ {{- $explicitPath -}}
+{{- else -}}
+ {{- .contextPath | default "/" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Returns the origin (scheme + host + port) for the service.
+Default ports (443/https, 80/http) are omitted from the URL.
+Usage: include "common.gateway.origin" .
+*/}}
+{{- define "common.gateway.origin" -}}
+{{- $scheme := include "common.gateway.scheme" . -}}
+{{- $host := include "common.gateway.hostname" . -}}
+{{- $port := include "common.gateway.externalPort" . -}}
+{{- printf "%s://%s" $scheme $host -}}
+{{- if ne $port (ternary "443" "80" (eq $scheme "https")) -}}:{{ $port }}{{- end -}}
+{{- end -}}
diff --git a/src/main/charts/confluence/.helmignore b/src/main/charts/confluence/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/confluence/.helmignore
+++ b/src/main/charts/confluence/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/confluence/Chart.yaml b/src/main/charts/confluence/Chart.yaml
index 6c35a4257..2d337ee1b 100644
--- a/src/main/charts/confluence/Chart.yaml
+++ b/src/main/charts/confluence/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: confluence
description: A chart for installing Confluence Data Center on Kubernetes
type: application
-version: '2.0.9'
+version: '2.0.10'
appVersion: 10.2.7
kubeVersion: ">=1.21.x-0"
keywords:
@@ -20,7 +20,7 @@ deprecated: false
annotations:
artifacthub.io/containsSecurityUpdates: "true"
artifacthub.io/changes: |-
- - "Update Helm chart version"
+ - "Add Gateway API support via HTTPRoute resources"
dependencies:
- name: common
diff --git a/src/main/charts/confluence/templates/NOTES.txt b/src/main/charts/confluence/templates/NOTES.txt
index be576cbc3..903b5f238 100644
--- a/src/main/charts/confluence/templates/NOTES.txt
+++ b/src/main/charts/confluence/templates/NOTES.txt
@@ -13,8 +13,8 @@ To see the custom values you used for this release:
$ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }}
-{{ if .Values.ingress.create -}}
-{{ title .Chart.Name }} service URL: {{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ include "confluence.ingressPath" . }}
+{{ if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{ title .Chart.Name }} service URL: {{ include "common.gateway.origin" . }}{{ include "confluence.path" . }}
{{- else }}
Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if contains "NodePort" .Values.confluence.service.type }}
diff --git a/src/main/charts/confluence/templates/_helpers.tpl b/src/main/charts/confluence/templates/_helpers.tpl
index 1514a247e..e13992f30 100644
--- a/src/main/charts/confluence/templates/_helpers.tpl
+++ b/src/main/charts/confluence/templates/_helpers.tpl
@@ -60,13 +60,6 @@
}
{{- end }}
-{{/*
-Create default value for ingress port
-*/}}
-{{- define "confluence.ingressPort" -}}
-{{ default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
-{{- end }}
-
{{/*
The name the synchrony app within the chart.
TODO: This will break if the common.names.name exceeds 63 characters, need to find a more rebust way to do this
@@ -205,10 +198,7 @@ Pod labels
{{- end }}
{{- if .Values.synchrony.enabled -}}
{{- if .Values.synchrony.service.url -}}-Dsynchrony.service.url={{ .Values.synchrony.service.url }}/v1
- {{- else -}}
- {{- if .Values.ingress.https -}}-Dsynchrony.service.url=https://{{ .Values.ingress.host }}/{{ $synchronyIngressPath }}/v1
- {{- else }}-Dsynchrony.service.url=http://{{ .Values.ingress.host }}/{{ $synchronyIngressPath }}/v1
- {{- end }}
+ {{- else -}}-Dsynchrony.service.url={{ include "common.gateway.scheme" . }}://{{ include "common.gateway.hostname" . }}/{{ $synchronyIngressPath }}/v1
{{- end }}
{{- else -}}
-Dsynchrony.btf.disabled=true
@@ -216,14 +206,22 @@ Pod labels
{{- end -}}
{{/*
-Create default value for ingress path
+Create default value for the service path.
*/}}
-{{- define "confluence.ingressPath" -}}
-{{- if .Values.ingress.path -}}
-{{- .Values.ingress.path -}}
-{{- else -}}
-{{ default ( "/" ) .Values.confluence.service.contextPath -}}
+{{- define "confluence.path" -}}
+{{- include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values.confluence.service.contextPath
+) -}}
{{- end }}
+
+{{/*
+Alias for backward compatibility with ingress templates.
+*/}}
+{{- define "confluence.ingressPath" -}}
+{{- include "confluence.path" . -}}
{{- end }}
{{/*
@@ -820,3 +818,5 @@ set -e; cp $JAVA_HOME/lib/security/cacerts /var/ssl/cacerts; chmod 664 /var/ssl/
key: OPENSEARCH_INITIAL_ADMIN_PASSWORD
{{- end }}
{{- end }}
+
+
diff --git a/src/main/charts/confluence/templates/common_templates b/src/main/charts/confluence/templates/common_templates
new file mode 120000
index 000000000..043f40ba4
--- /dev/null
+++ b/src/main/charts/confluence/templates/common_templates
@@ -0,0 +1 @@
+../../common_templates
\ No newline at end of file
diff --git a/src/main/charts/confluence/templates/configmap-server-config.yaml b/src/main/charts/confluence/templates/configmap-server-config.yaml
index 5152f6b03..5e5b41063 100644
--- a/src/main/charts/confluence/templates/configmap-server-config.yaml
+++ b/src/main/charts/confluence/templates/configmap-server-config.yaml
@@ -25,10 +25,10 @@ data:
acceptCount="{{ .Values.confluence.tomcatConfig.acceptCount | default "100" }}"
debug="{{ .Values.confluence.tomcatConfig.debug | default "0" }}"
URIEncoding="{{ .Values.confluence.tomcatConfig.uriEncoding | default "UTF-8" }}"
- secure="{{ default (ternary "true" "false" .Values.ingress.https) .Values.confluence.tomcatConfig.secure }}"
- scheme="{{ default (ternary "https" "http" .Values.ingress.https) .Values.confluence.tomcatConfig.scheme }}"
- proxyName="{{ .Values.confluence.tomcatConfig.proxyName | default .Values.ingress.host }}"
- proxyPort="{{ .Values.confluence.tomcatConfig.proxyPort | default (ternary "443" "80" .Values.ingress.https) }}"
+ secure="{{ default (include "common.gateway.https" .) .Values.confluence.tomcatConfig.secure }}"
+ scheme="{{ default (include "common.gateway.scheme" .) .Values.confluence.tomcatConfig.scheme }}"
+ proxyName="{{ .Values.confluence.tomcatConfig.proxyName | default (include "common.gateway.hostname" .) }}"
+ proxyPort="{{ .Values.confluence.tomcatConfig.proxyPort | default (include "common.gateway.externalPort" .) }}"
maxHttpHeaderSize="{{ .Values.confluence.tomcatConfig.maxHttpHeaderSize | default "8192" }}" />
{{- if .Values.confluence.tunnel.additionalConnector.port }}
diff --git a/src/main/charts/confluence/templates/httproute.yaml b/src/main/charts/confluence/templates/httproute.yaml
new file mode 100644
index 000000000..de93e950a
--- /dev/null
+++ b/src/main/charts/confluence/templates/httproute.yaml
@@ -0,0 +1,67 @@
+{{- if .Values.gateway.create }}
+{{- include "common.gateway.validateConfig" . -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ include "common.names.fullname" . }}
+ labels:
+ {{- include "common.labels.commonLabels" . | nindent 4 }}
+ {{- with .Values.gateway.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.gateway.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ # Reference to the Gateway
+ parentRefs:
+ {{- toYaml .Values.gateway.parentRefs | nindent 2 }}
+
+ # Hostnames to match
+ hostnames:
+ {{- range .Values.gateway.hostnames }}
+ - {{ . | quote }}
+ {{- end }}
+
+ # Routing rules
+ rules:
+
+ {{- if .Values.synchrony.enabled }}
+ # Synchrony rule (collaborative editing)
+ - matches:
+ - path:
+ type: PathPrefix
+ value: {{ .Values.synchrony.ingress.path | default "/synchrony" }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "synchrony.fullname" . }}
+ port: {{ .Values.synchrony.service.port }}
+ weight: 100
+ {{- end }}
+
+ # Default rule - routes to Confluence service
+ - matches:
+ - path:
+ type: {{ .Values.gateway.pathType }}
+ value: {{ include "confluence.path" . }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ {{- with .Values.gateway.filters }}
+ filters:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "common.names.fullname" . }}
+ port: {{ .Values.confluence.service.port }}
+ weight: 100
+
+ {{- with .Values.gateway.additionalRules }}
+ {{- toYaml . | nindent 2 }}
+ {{- end }}
+{{- end }}
diff --git a/src/main/charts/confluence/templates/statefulset-synchrony.yaml b/src/main/charts/confluence/templates/statefulset-synchrony.yaml
index 585a123cb..b1b1a2b24 100644
--- a/src/main/charts/confluence/templates/statefulset-synchrony.yaml
+++ b/src/main/charts/confluence/templates/statefulset-synchrony.yaml
@@ -131,11 +131,7 @@ spec:
{{ if .Values.synchrony.service.url }}
value: "{{ .Values.synchrony.service.url }}"
{{ else }}
- {{ if .Values.ingress.https }}
- value: "https://{{ .Values.ingress.host }}/synchrony"
- {{ else }}
- value: "http://{{ .Values.ingress.host }}/synchrony"
- {{- end }}
+ value: "{{ include "common.gateway.scheme" . }}://{{ include "common.gateway.hostname" . }}/synchrony"
{{ end }}
{{- include "synchrony.databaseEnvVars" . | nindent 12 }}
{{- include "synchrony.clusteringEnvVars" . | nindent 12 }}
diff --git a/src/main/charts/confluence/templates/statefulset.yaml b/src/main/charts/confluence/templates/statefulset.yaml
index 588796af6..836e94b30 100644
--- a/src/main/charts/confluence/templates/statefulset.yaml
+++ b/src/main/charts/confluence/templates/statefulset.yaml
@@ -1,3 +1,4 @@
+{{- include "common.gateway.validateConfig" . -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -212,7 +213,7 @@ spec:
env:
{{- include "confluence.sessionVars" . | nindent 12 }}
{{- include "confluence.tunnelVars" . | nindent 12 }}
- {{ if .Values.ingress.https }}
+ {{ if eq (include "common.gateway.https" .) "true" }}
- name: ATL_TOMCAT_SCHEME
value: "https"
- name: ATL_TOMCAT_SECURE
@@ -224,11 +225,11 @@ spec:
{{ end }}
- name: ATL_TOMCAT_PORT
value: {{ .Values.confluence.ports.http | quote }}
- {{ if .Values.ingress.host }}
+ {{ if eq (include "common.gateway.isConfigured" .) "true" }}
- name: ATL_PROXY_NAME
- value: {{ .Values.ingress.host | quote }}
+ value: {{ include "common.gateway.hostname" . | quote }}
- name: ATL_PROXY_PORT
- value: {{ include "confluence.ingressPort" . | quote }}
+ value: {{ include "common.gateway.externalPort" . | quote }}
{{ end }}
- name: ATL_TOMCAT_ACCESS_LOG
value: {{ .Values.confluence.accessLog.enabled | quote }}
diff --git a/src/main/charts/confluence/values.yaml b/src/main/charts/confluence/values.yaml
index 3ecf479cd..6f80f3184 100644
--- a/src/main/charts/confluence/values.yaml
+++ b/src/main/charts/confluence/values.yaml
@@ -11,10 +11,11 @@
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#3-configure-database
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#5-configure-persistent-storage
#
-# To manage external access to the Confluence instance, an ingress resource can also be configured
-# under the 'ingress' stanza. This requires a pre-provisioned ingress controller to be present.
+# To manage external access to the Confluence instance, a Gateway API HTTPRoute or an Ingress
+# resource can be configured under the 'gateway' or 'ingress' stanza respectively.
+# This requires a pre-provisioned gateway/ingress controller to be present.
#
-# Additional details on pre-provisioning an ingress controller can be found here:
+# Additional details on configuring external access can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#4-configure-ingress
#
##
@@ -472,9 +473,9 @@ volumes:
# Ingress configuration
#
-# To make the Atlassian product available from outside the K8s cluster an Ingress
-# Controller should be pre-provisioned. With this in place the configuration below
-# can be used to configure an appropriate Ingress Resource.
+# Use this section when routing external traffic to Confluence via a Kubernetes Ingress
+# resource (K8s Ingress API). Requires a pre-provisioned Ingress Controller.
+# If using the Gateway API instead, see the 'gateway' section below.
# https://atlassian.github.io/data-center-helm-charts/userguide/CONFIGURATION/#ingress
#
ingress:
@@ -484,6 +485,27 @@ ingress:
#
create: false
+ # -- The fully-qualified hostname (FQDN) of the Confluence instance. This value is used
+ # to configure the product's proxy settings and, when ingress.create is true,
+ # the Ingress resource routing rules.
+ #
+ host:
+
+ # -- Whether users access the application over HTTPS. Set to 'false' if not
+ # using TLS, e.g. when reaching the service via localhost port-forwarding.
+ #
+ https: true
+
+ # -- The base path for the application, e.g. '/confluence'.
+ # Defaults to 'confluence.service.contextPath'.
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when ingress.create is true.
+ # They configure the Ingress resource itself and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
# -- Set to true if you want to create an OpenShift Route instead of an Ingress
#
openShiftRoute: false
@@ -535,28 +557,12 @@ ingress:
#
proxySendTimeout: 60
- # -- The fully-qualified hostname (FQDN) of the Ingress Resource. Traffic coming in on
- # this hostname will be routed by the Ingress Resource to the appropriate backend
- # Service.
- #
- host:
-
- # -- The base path for the Ingress Resource. For example '/confluence'. Based on a
- # 'ingress.host' value of 'company.k8s.com' this would result in a URL of
- # 'company.k8s.com/confluence'. Default value is 'confluence.service.contextPath'
- path:
-
# -- The custom annotations that should be applied to the Ingress Resource.
# If using an ingress-nginx controller be sure that the annotations you add
# here are compatible with those already defined in the 'ingess.yaml' template
#
annotations: {}
- # -- Set to 'true' if browser communication with the application should be TLS
- # (HTTPS) enforced.
- #
- https: true
-
# -- The name of the K8s Secret that contains the TLS private key and corresponding
# certificate. When utilised, TLS termination occurs at the ingress point where
# traffic to the Service, and it's Pods is in plaintext.
@@ -576,6 +582,120 @@ ingress:
# service: static-content-svc
# portNumber: 80
+# Gateway API configuration
+#
+# Use this section when routing external traffic to Confluence via the Kubernetes
+# Gateway API, or when using an external proxy/load balancer without creating
+# any K8s routing resource. Only one of 'ingress' or 'gateway' should be used.
+# https://gateway-api.sigs.k8s.io/
+#
+gateway:
+
+ # -- Set to 'true' if an HTTPRoute Resource should be created. This depends on a
+ # pre-provisioned Gateway API controller being available and a Gateway resource.
+ # Cannot be enabled if ingress.create is true.
+ #
+ create: false
+
+ # -- The hostnames that should be routed to Confluence. At least one hostname
+ # is required when gateway.create is true. Setting hostnames activates gateway
+ # mode for product configuration even when gateway.create is false, allowing
+ # use with a pre-existing Gateway or external proxy.
+ # The first entry is used as the canonical hostname for base URL, proxy
+ # settings, and NOTES output — list the primary/public hostname first.
+ #
+ hostnames: []
+ # - confluence.example.com
+ # - wiki.example.com
+
+ # -- Whether users access the application over HTTPS.
+ # This does not configure TLS on the Gateway or load balancer — it must match
+ # how traffic is actually routed to the application.
+ #
+ https: true
+
+ # -- The port users connect on. Defaults to 443 (https) or 80 (http).
+ # This does not change the Gateway or load balancer port — it must match
+ # the port that is actually used. Only set for non-standard ports.
+ #
+ # externalPort:
+
+ # -- The base path for routing. When empty, falls back to the product's
+ # service.contextPath (same behavior as ingress). Set explicitly to
+ # override, e.g. "/confluence".
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when gateway.create is true.
+ # They configure the HTTPRoute resource and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
+ # -- Reference to the parent Gateway resource. Supports any standard
+ # parentRef fields (name, namespace, sectionName, etc.).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ #
+ parentRefs: []
+ # - name: example-gateway
+ # namespace: example-gateway-namespace
+ # sectionName: https
+
+ # -- Path matching type. Can be "PathPrefix", "Exact", or "RegularExpression".
+ # PathPrefix is recommended for most use cases.
+ #
+ pathType: "PathPrefix"
+
+ # -- Annotations to add to the HTTPRoute resource.
+ #
+ annotations: {}
+ # kubernetes.io/ingress.class: gateway
+ # cert-manager.io/cluster-issuer: letsencrypt
+
+ # -- Labels to add to the HTTPRoute resource.
+ #
+ labels: {}
+ # environment: production
+ # team: platform
+
+ # -- HTTP filters to apply to requests. Can be used to add/remove headers,
+ # perform redirects, or rewrite URLs.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter
+ #
+ filters: []
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # add:
+ # - name: X-Forwarded-Proto
+ # value: https
+
+ # -- Advanced routing rules. Use this for complex routing scenarios like
+ # header-based routing, traffic splitting, or multiple backends.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ #
+ additionalRules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api
+ # backendRefs:
+ # - name: confluence-api
+ # port: 8090
+
+ # -- Session affinity (sticky sessions) is required for Atlassian DC products but
+ # is not part of the standard Gateway API HTTPRoute spec. It must be configured
+ # separately through your Gateway implementation's own policy resources.
+ # See docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md for working examples.
+
+ # -- Timeout configuration for HTTPRoute rules.
+ # Note: when migrating from Ingress, these replace proxyReadTimeout and
+ # proxySendTimeout. There is no Gateway API equivalent for
+ # proxyConnectTimeout or maxBodySize — those require controller-specific
+ # policies (e.g. Envoy Gateway BackendTrafficPolicy).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteTimeouts
+ #
+ timeouts:
+ request: "60s"
+ backendRequest: "60s"
# Confluence configuration
#
@@ -973,13 +1093,13 @@ confluence:
acceptCount: "100"
debug: "0"
uriEncoding: "UTF-8"
- # secure is retrieved from ingress.https value
+ # secure is set based on the https setting (gateway.https or ingress.https)
secure:
- # scheme is set depending on ingress.https value (http if false, https if true)
+ # scheme is set based on the https setting (http if false, https if true)
scheme:
- # proxyName is retrieved from ingress.host value
+ # proxyName is set to the configured hostname (gateway.hostnames[0] or ingress.host)
proxyName:
- # proxyPort is set depending on ingress.https value (80 if http, 443 if https)
+ # proxyPort is set to the external port (defaults to 443 for https, 80 for http)
proxyPort:
maxHttpHeaderSize: "8192"
proxyInternalIps:
diff --git a/src/main/charts/crowd/.helmignore b/src/main/charts/crowd/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/crowd/.helmignore
+++ b/src/main/charts/crowd/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/crowd/Chart.yaml b/src/main/charts/crowd/Chart.yaml
index d733539bc..8fa329509 100644
--- a/src/main/charts/crowd/Chart.yaml
+++ b/src/main/charts/crowd/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: crowd
description: A chart for installing Crowd Data Center on Kubernetes
type: application
-version: '2.0.9'
+version: '2.0.10'
appVersion: 7.1.5
kubeVersion: ">=1.21.x-0"
@@ -21,7 +21,8 @@ deprecated: false
annotations:
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: |-
- - "Update appVersions for DC apps (#1079)"
+ - "Add Gateway API support via HTTPRoute resources"
+ - "Gateway API provides modern alternative to Ingress"
dependencies:
- name: common
diff --git a/src/main/charts/crowd/templates/NOTES.txt b/src/main/charts/crowd/templates/NOTES.txt
index c9e9bf64d..f235237f9 100644
--- a/src/main/charts/crowd/templates/NOTES.txt
+++ b/src/main/charts/crowd/templates/NOTES.txt
@@ -13,8 +13,8 @@ To see the custom values you used for this release:
$ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }}
-{{ if .Values.ingress.create -}}
-{{ title .Chart.Name }} service URL: {{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ default ( "" ) .Values.ingress.path }}
+{{ if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{ title .Chart.Name }} service URL: {{ include "common.gateway.origin" . }}{{ include "crowd.path" . }}
{{- else }}
Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if contains "NodePort" .Values.crowd.service.type }}
diff --git a/src/main/charts/crowd/templates/_helpers.tpl b/src/main/charts/crowd/templates/_helpers.tpl
index 10f98d061..0134daa8a 100644
--- a/src/main/charts/crowd/templates/_helpers.tpl
+++ b/src/main/charts/crowd/templates/_helpers.tpl
@@ -46,13 +46,6 @@
}
{{- end }}
-{{/*
-Create default value for ingress port
-*/}}
-{{- define "crowd.ingressPort" -}}
-{{ default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
-{{- end }}
-
{{/*
The name of the service account to be used.
If the name is defined in the chart values, then use that,
@@ -372,3 +365,18 @@ Define additional hosts here to allow template overrides when used as a sub char
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 }}
+
+
+
+{{/*
+Create default value for the service path.
+*/}}
+{{- define "crowd.path" -}}
+{{- include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values.crowd.service.contextPath
+) -}}
+{{- end }}
+
diff --git a/src/main/charts/crowd/templates/common_templates b/src/main/charts/crowd/templates/common_templates
new file mode 120000
index 000000000..043f40ba4
--- /dev/null
+++ b/src/main/charts/crowd/templates/common_templates
@@ -0,0 +1 @@
+../../common_templates
\ No newline at end of file
diff --git a/src/main/charts/crowd/templates/configmap-server-config.yaml b/src/main/charts/crowd/templates/configmap-server-config.yaml
index 26f6c7472..d613112f8 100644
--- a/src/main/charts/crowd/templates/configmap-server-config.yaml
+++ b/src/main/charts/crowd/templates/configmap-server-config.yaml
@@ -32,10 +32,10 @@ data:
protocol="{{ .Values.crowd.tomcatConfig.protocol | default "HTTP/1.1" }}"
redirectPort="{{ .Values.crowd.tomcatConfig.redirectPort | default "8443" }}"
acceptCount="{{ .Values.crowd.tomcatConfig.acceptCount | default "100" }}"
- secure="{{ default (ternary "true" "false" .Values.ingress.https) .Values.crowd.tomcatConfig.secure }}"
- scheme="{{ default (ternary "https" "http" .Values.ingress.https) .Values.crowd.tomcatConfig.scheme }}"
- proxyName="{{ .Values.crowd.tomcatConfig.proxyName | default .Values.ingress.host }}"
- proxyPort="{{ .Values.crowd.tomcatConfig.proxyPort | default (ternary "443" "80" .Values.ingress.https) }}"
+ secure="{{ default (include "common.gateway.https" .) .Values.crowd.tomcatConfig.secure }}"
+ scheme="{{ default (include "common.gateway.scheme" .) .Values.crowd.tomcatConfig.scheme }}"
+ proxyName="{{ .Values.crowd.tomcatConfig.proxyName | default (include "common.gateway.hostname" .) }}"
+ proxyPort="{{ .Values.crowd.tomcatConfig.proxyPort | default (include "common.gateway.externalPort" .) }}"
maxHttpHeaderSize="{{ .Values.crowd.tomcatConfig.maxHttpHeaderSize | default "8192" }}"
useBodyEncodingForURI="true"
URIEncoding="UTF-8"
diff --git a/src/main/charts/crowd/templates/httproute.yaml b/src/main/charts/crowd/templates/httproute.yaml
new file mode 100644
index 000000000..528d49eab
--- /dev/null
+++ b/src/main/charts/crowd/templates/httproute.yaml
@@ -0,0 +1,50 @@
+{{- if .Values.gateway.create }}
+{{- include "common.gateway.validateConfig" . -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ include "common.names.fullname" . }}
+ labels:
+ {{- include "common.labels.commonLabels" . | nindent 4 }}
+ {{- with .Values.gateway.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.gateway.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ # Reference to the Gateway
+ parentRefs:
+ {{- toYaml .Values.gateway.parentRefs | nindent 2 }}
+
+ # Hostnames to match
+ hostnames:
+ {{- range .Values.gateway.hostnames }}
+ - {{ . | quote }}
+ {{- end }}
+
+ # Routing rules
+ rules:
+ # Default rule - routes to Crowd service
+ - matches:
+ - path:
+ type: {{ .Values.gateway.pathType }}
+ value: {{ include "crowd.path" . }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ {{- with .Values.gateway.filters }}
+ filters:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "common.names.fullname" . }}
+ port: {{ .Values.crowd.service.port }}
+ weight: 100
+
+ {{- with .Values.gateway.additionalRules }}
+ {{- toYaml . | nindent 2 }}
+ {{- end }}
+{{- end }}
diff --git a/src/main/charts/crowd/templates/statefulset.yaml b/src/main/charts/crowd/templates/statefulset.yaml
index ae1b74f2d..2460f8e8a 100644
--- a/src/main/charts/crowd/templates/statefulset.yaml
+++ b/src/main/charts/crowd/templates/statefulset.yaml
@@ -1,3 +1,4 @@
+{{- include "common.gateway.validateConfig" . -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -192,7 +193,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
- {{ if .Values.ingress.https }}
+ {{ if eq (include "common.gateway.https" .) "true" }}
- name: ATL_TOMCAT_SCHEME
value: "https"
- name: ATL_TOMCAT_SECURE
@@ -204,11 +205,11 @@ spec:
{{ end }}
- name: ATL_TOMCAT_PORT
value: {{ .Values.crowd.ports.http | quote }}
- {{ if .Values.ingress.host }}
+ {{ if eq (include "common.gateway.isConfigured" .) "true" }}
- name: ATL_PROXY_NAME
- value: {{ .Values.ingress.host | quote }}
+ value: {{ include "common.gateway.hostname" . | quote }}
- name: ATL_PROXY_PORT
- value: {{ include "crowd.ingressPort" . | quote }}
+ value: {{ include "common.gateway.externalPort" . | quote }}
{{ end }}
- name: ATL_TOMCAT_ACCESS_LOG
value: {{ .Values.crowd.accessLog.enabled | quote }}
diff --git a/src/main/charts/crowd/values.yaml b/src/main/charts/crowd/values.yaml
index 6458e5e05..caede1952 100644
--- a/src/main/charts/crowd/values.yaml
+++ b/src/main/charts/crowd/values.yaml
@@ -9,10 +9,11 @@
# Additional details on pre-provisioning these required resources can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#5-configure-persistent-storage
#
-# To manage external access to the Crowd instance, an ingress resource can also be configured
-# under the 'ingress' stanza. This requires a pre-provisioned ingress controller to be present.
+# To manage external access to the Crowd instance, a Gateway API HTTPRoute or an Ingress
+# resource can be configured under the 'gateway' or 'ingress' stanza respectively.
+# This requires a pre-provisioned gateway/ingress controller to be present.
#
-# Additional details on pre-provisioning an ingress controller can be found here:
+# Additional details on configuring external access can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#4-configure-ingress
#
##
@@ -384,13 +385,13 @@ crowd:
protocol: "HTTP/1.1"
redirectPort: "8443"
acceptCount: "100"
- # secure is retrieved from ingress.https value
+ # secure is set based on the https setting (gateway.https or ingress.https)
secure:
- # scheme is set depending on ingress.https value (http if false, https if true)
+ # scheme is set based on the https setting (http if false, https if true)
scheme:
- # proxyName is retrieved from ingress.host value
+ # proxyName is set to the configured hostname (gateway.hostnames[0] or ingress.host)
proxyName:
- # proxyPort is set depending on ingress.https value (80 if http, 443 if https)
+ # proxyPort is set to the external port (defaults to 443 for https, 80 for http)
proxyPort:
maxHttpHeaderSize: "8192"
accessLogsMaxDays: "-1"
@@ -500,9 +501,9 @@ crowd:
# Ingress configuration
#
-# To make the Atlassian product available from outside the K8s cluster an Ingress
-# Controller should be pre-provisioned. With this in place the configuration below
-# can be used to configure an appropriate Ingress Resource.
+# Use this section when routing external traffic to Crowd via a Kubernetes Ingress
+# resource (K8s Ingress API). Requires a pre-provisioned Ingress Controller.
+# If using the Gateway API instead, see the 'gateway' section below.
# https://atlassian.github.io/data-center-helm-charts/userguide/CONFIGURATION/#ingress
#
ingress:
@@ -512,6 +513,26 @@ ingress:
#
create: false
+ # -- The fully-qualified hostname (FQDN) of the Crowd instance. This value is used
+ # to configure the product's proxy settings and, when ingress.create is true,
+ # the Ingress resource routing rules.
+ #
+ host:
+
+ # -- Whether users access the application over HTTPS. Set to 'false' if not
+ # using TLS, e.g. when reaching the service via localhost port-forwarding.
+ #
+ https: true
+
+ # -- The base path for the application, e.g. '/crowd'.
+ #
+ path: "/"
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when ingress.create is true.
+ # They configure the Ingress resource itself and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
# -- Set to true if you want to create an OpenShift Route instead of an Ingress
#
openShiftRoute: false
@@ -563,29 +584,12 @@ ingress:
#
proxySendTimeout: 60
- # -- The fully-qualified hostname (FQDN) of the Ingress Resource. Traffic coming in on
- # this hostname will be routed by the Ingress Resource to the appropriate backend
- # Service.
- #
- host:
-
- # -- The base path for the Ingress Resource. For example '/crowd'. Based on
- # 'ingress.host' value of 'company.k8s.com' this would result in a URL of
- # 'company.k8s.com/crowd'.
- #
- path: "/"
-
# -- The custom annotations that should be applied to the Ingress Resource.
# If using an ingress-nginx controller be sure that the annotations you add
# here are compatible with those already defined in the 'ingess.yaml' template
#
annotations: {}
- # -- Set to 'true' if browser communication with the application should be TLS
- # (HTTPS) enforced.
- #
- https: true
-
# -- The name of the K8s Secret that contains the TLS private key and corresponding
# certificate. When utilised, TLS termination occurs at the ingress point where
# traffic to the Service, and it's Pods is in plaintext.
@@ -605,6 +609,120 @@ ingress:
# service: static-content-svc
# portNumber: 80
+# Gateway API configuration
+#
+# Use this section when routing external traffic to Crowd via the Kubernetes
+# Gateway API, or when using an external proxy/load balancer without creating
+# any K8s routing resource. Only one of 'ingress' or 'gateway' should be used.
+# https://gateway-api.sigs.k8s.io/
+#
+gateway:
+
+ # -- Set to 'true' if an HTTPRoute Resource should be created. This depends on a
+ # pre-provisioned Gateway API controller being available and a Gateway resource.
+ # Cannot be enabled if ingress.create is true.
+ #
+ create: false
+
+ # -- The hostnames that should be routed to Crowd. At least one hostname
+ # is required when gateway.create is true. Setting hostnames activates gateway
+ # mode for product configuration even when gateway.create is false, allowing
+ # use with a pre-existing Gateway or external proxy.
+ # The first entry is used as the canonical hostname for base URL, proxy
+ # settings, and NOTES output — list the primary/public hostname first.
+ #
+ hostnames: []
+ # - crowd.example.com
+ # - identity.example.com
+
+ # -- Whether users access the application over HTTPS.
+ # This does not configure TLS on the Gateway or load balancer — it must match
+ # how traffic is actually routed to the application.
+ #
+ https: true
+
+ # -- The port users connect on. Defaults to 443 (https) or 80 (http).
+ # This does not change the Gateway or load balancer port — it must match
+ # the port that is actually used. Only set for non-standard ports.
+ #
+ # externalPort:
+
+ # -- The base path for routing. When empty, falls back to the product's
+ # service.contextPath (same behavior as ingress). Set explicitly to
+ # override, e.g. "/crowd".
+ #
+ path: "/"
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when gateway.create is true.
+ # They configure the HTTPRoute resource and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
+ # -- Reference to the parent Gateway resource. Supports any standard
+ # parentRef fields (name, namespace, sectionName, etc.).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ #
+ parentRefs: []
+ # - name: example-gateway
+ # namespace: example-gateway-namespace
+ # sectionName: https
+
+ # -- Path matching type. Can be "PathPrefix", "Exact", or "RegularExpression".
+ # PathPrefix is recommended for most use cases.
+ #
+ pathType: "PathPrefix"
+
+ # -- Annotations to add to the HTTPRoute resource.
+ #
+ annotations: {}
+ # kubernetes.io/ingress.class: gateway
+ # cert-manager.io/cluster-issuer: letsencrypt
+
+ # -- Labels to add to the HTTPRoute resource.
+ #
+ labels: {}
+ # environment: production
+ # team: platform
+
+ # -- HTTP filters to apply to requests. Can be used to add/remove headers,
+ # perform redirects, or rewrite URLs.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter
+ #
+ filters: []
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # add:
+ # - name: X-Forwarded-Proto
+ # value: https
+
+ # -- Advanced routing rules. Use this for complex routing scenarios like
+ # header-based routing, traffic splitting, or multiple backends.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ #
+ additionalRules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api
+ # backendRefs:
+ # - name: crowd-api
+ # port: 8095
+
+ # -- Session affinity (sticky sessions) is required for Atlassian DC products but
+ # is not part of the standard Gateway API HTTPRoute spec. It must be configured
+ # separately through your Gateway implementation's own policy resources.
+ # See docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md for working examples.
+
+ # -- Timeout configuration for HTTPRoute rules.
+ # Note: when migrating from Ingress, these replace proxyReadTimeout and
+ # proxySendTimeout. There is no Gateway API equivalent for
+ # proxyConnectTimeout or maxBodySize — those require controller-specific
+ # policies (e.g. Envoy Gateway BackendTrafficPolicy).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteTimeouts
+ #
+ timeouts:
+ request: "60s"
+ backendRequest: "60s"
# Fluentd configuration
#
diff --git a/src/main/charts/jira/.helmignore b/src/main/charts/jira/.helmignore
index 0e8a0eb36..c7423efa9 100644
--- a/src/main/charts/jira/.helmignore
+++ b/src/main/charts/jira/.helmignore
@@ -21,3 +21,5 @@
.idea/
*.tmproj
.vscode/
+# Ignore non-template files from symlinked common_templates
+templates/common_templates/*.md
diff --git a/src/main/charts/jira/Chart.yaml b/src/main/charts/jira/Chart.yaml
index e2107d34d..15483692c 100644
--- a/src/main/charts/jira/Chart.yaml
+++ b/src/main/charts/jira/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: jira
description: A chart for installing Jira Data Center on Kubernetes
type: application
-version: '2.0.9'
+version: '2.0.10'
appVersion: 11.3.3
kubeVersion: ">=1.21.x-0"
keywords:
@@ -22,7 +22,8 @@ deprecated: false
annotations:
artifacthub.io/containsSecurityUpdates: "false"
artifacthub.io/changes: |-
- - "Update Helm chart version"
+ - "Add Gateway API support via HTTPRoute resources"
+ - "Gateway API provides modern alternative to Ingress"
dependencies:
- name: common
diff --git a/src/main/charts/jira/templates/NOTES.txt b/src/main/charts/jira/templates/NOTES.txt
index f3323c049..2ed8f854b 100644
--- a/src/main/charts/jira/templates/NOTES.txt
+++ b/src/main/charts/jira/templates/NOTES.txt
@@ -13,8 +13,8 @@ To see the custom values you used for this release:
$ helm get values {{ .Release.Name }} -n {{ .Release.Namespace }}
-{{- if .Values.ingress.create -}}
-{{ title .Chart.Name }} service URL: {{ ternary "https" "http" .Values.ingress.https -}}://{{ .Values.ingress.host }}{{ include "jira.ingressPath" . }}
+{{- if eq (include "common.gateway.isConfigured" .) "true" -}}
+{{ title .Chart.Name }} service URL: {{ include "common.gateway.origin" . }}{{ include "jira.path" . }}
{{- else }}
Get the {{ title .Chart.Name }} URL by running these commands in the same shell:
{{- if contains "NodePort" .Values.jira.service.type }}
diff --git a/src/main/charts/jira/templates/_helpers.tpl b/src/main/charts/jira/templates/_helpers.tpl
index 4f9cc1b20..075dd99b2 100644
--- a/src/main/charts/jira/templates/_helpers.tpl
+++ b/src/main/charts/jira/templates/_helpers.tpl
@@ -109,21 +109,22 @@
{{- end }}
{{/*
-Create default value for ingress port
+Create default value for the service path.
*/}}
-{{- define "jira.ingressPort" -}}
-{{ default (ternary "443" "80" .Values.ingress.https) .Values.ingress.port -}}
+{{- define "jira.path" -}}
+{{- include "common.gateway.path" (dict
+ "useGatewayMode" (include "common.gateway.useGatewayMode" .)
+ "gatewayPath" .Values.gateway.path
+ "ingressPath" .Values.ingress.path
+ "contextPath" .Values.jira.service.contextPath
+) -}}
{{- end }}
{{/*
-Create default value for ingress path
+Alias for backward compatibility with ingress templates.
*/}}
{{- define "jira.ingressPath" -}}
-{{- if .Values.ingress.path -}}
-{{- .Values.ingress.path -}}
-{{- else -}}
-{{ default ( "/" ) .Values.jira.service.contextPath -}}
-{{- end }}
+{{- include "jira.path" . -}}
{{- end }}
{{/*
@@ -545,3 +546,5 @@ 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 }}
+
+
diff --git a/src/main/charts/jira/templates/common_templates b/src/main/charts/jira/templates/common_templates
new file mode 120000
index 000000000..043f40ba4
--- /dev/null
+++ b/src/main/charts/jira/templates/common_templates
@@ -0,0 +1 @@
+../../common_templates
\ No newline at end of file
diff --git a/src/main/charts/jira/templates/configmap-server-config.yaml b/src/main/charts/jira/templates/configmap-server-config.yaml
index 480f9ba43..cc258c3d6 100644
--- a/src/main/charts/jira/templates/configmap-server-config.yaml
+++ b/src/main/charts/jira/templates/configmap-server-config.yaml
@@ -32,10 +32,10 @@ data:
protocol="{{ .Values.jira.tomcatConfig.protocol | default "HTTP/1.1" }}"
redirectPort="{{ .Values.jira.tomcatConfig.redirectPort | default "8443" }}"
acceptCount="{{ .Values.jira.tomcatConfig.acceptCount | default "100" }}"
- secure="{{ default (ternary "true" "false" .Values.ingress.https) .Values.jira.tomcatConfig.secure }}"
- scheme="{{ default (ternary "https" "http" .Values.ingress.https) .Values.jira.tomcatConfig.scheme }}"
- proxyName="{{ .Values.jira.tomcatConfig.proxyName | default .Values.ingress.host }}"
- proxyPort="{{ .Values.jira.tomcatConfig.proxyPort | default (ternary "443" "80" .Values.ingress.https) }}"
+ secure="{{ default (include "common.gateway.https" .) .Values.jira.tomcatConfig.secure }}"
+ scheme="{{ default (include "common.gateway.scheme" .) .Values.jira.tomcatConfig.scheme }}"
+ proxyName="{{ .Values.jira.tomcatConfig.proxyName | default (include "common.gateway.hostname" .) }}"
+ proxyPort="{{ .Values.jira.tomcatConfig.proxyPort | default (include "common.gateway.externalPort" .) }}"
relaxedPathChars="[]|"
relaxedQueryChars="[]|{}^\`"<>"
diff --git a/src/main/charts/jira/templates/httproute.yaml b/src/main/charts/jira/templates/httproute.yaml
new file mode 100644
index 000000000..afd6b7e5e
--- /dev/null
+++ b/src/main/charts/jira/templates/httproute.yaml
@@ -0,0 +1,50 @@
+{{- if .Values.gateway.create }}
+{{- include "common.gateway.validateConfig" . -}}
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: {{ include "common.names.fullname" . }}
+ labels:
+ {{- include "common.labels.commonLabels" . | nindent 4 }}
+ {{- with .Values.gateway.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- with .Values.gateway.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ # Reference to the Gateway
+ parentRefs:
+ {{- toYaml .Values.gateway.parentRefs | nindent 2 }}
+
+ # Hostnames to match
+ hostnames:
+ {{- range .Values.gateway.hostnames }}
+ - {{ . | quote }}
+ {{- end }}
+
+ # Routing rules
+ rules:
+ # Default rule - routes to Jira service
+ - matches:
+ - path:
+ type: {{ .Values.gateway.pathType }}
+ value: {{ include "jira.path" . }}
+ {{- with .Values.gateway.timeouts }}
+ timeouts:
+ {{- toYaml . | nindent 6 }}
+ {{- end }}
+ {{- with .Values.gateway.filters }}
+ filters:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ backendRefs:
+ - name: {{ include "common.names.fullname" . }}
+ port: {{ .Values.jira.service.port }}
+ weight: 100
+
+ {{- with .Values.gateway.additionalRules }}
+ {{- toYaml . | nindent 2 }}
+ {{- end }}
+{{- end }}
diff --git a/src/main/charts/jira/templates/statefulset.yaml b/src/main/charts/jira/templates/statefulset.yaml
index bf92fe615..d03217ac0 100644
--- a/src/main/charts/jira/templates/statefulset.yaml
+++ b/src/main/charts/jira/templates/statefulset.yaml
@@ -1,3 +1,4 @@
+{{- include "common.gateway.validateConfig" . -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -119,7 +120,7 @@ spec:
env:
{{- include "jira.sessionVars" . | nindent 12 }}
{{- include "jira.tunnelVars" . | nindent 12 }}
- {{ if .Values.ingress.https }}
+ {{ if eq (include "common.gateway.https" .) "true" }}
- name: ATL_TOMCAT_SCHEME
value: "https"
- name: ATL_TOMCAT_SECURE
@@ -131,11 +132,11 @@ spec:
{{ end }}
- name: ATL_TOMCAT_PORT
value: {{ .Values.jira.ports.http | quote }}
- {{ if .Values.ingress.host }}
+ {{ if eq (include "common.gateway.isConfigured" .) "true" }}
- name: ATL_PROXY_NAME
- value: {{ .Values.ingress.host | quote }}
+ value: {{ include "common.gateway.hostname" . | quote }}
- name: ATL_PROXY_PORT
- value: {{ include "jira.ingressPort" . | quote }}
+ value: {{ include "common.gateway.externalPort" . | quote }}
{{ end }}
{{- include "jira.s3StorageEnvVars" . | nindent 12 }}
{{- include "jira.databaseEnvVars" . | nindent 12 }}
diff --git a/src/main/charts/jira/values.yaml b/src/main/charts/jira/values.yaml
index 8ef066712..903371656 100644
--- a/src/main/charts/jira/values.yaml
+++ b/src/main/charts/jira/values.yaml
@@ -11,10 +11,11 @@
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#3-configure-database
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#5-configure-persistent-storage
#
-# To manage external access to the Jira instance, an ingress resource can also be configured
-# under the 'ingress' stanza. This requires a pre-provisioned ingress controller to be present.
+# To manage external access to the Jira instance, a Gateway API HTTPRoute or an Ingress
+# resource can be configured under the 'gateway' or 'ingress' stanza respectively.
+# This requires a pre-provisioned gateway/ingress controller to be present.
#
-# Additional details on pre-provisioning an ingress controller can be found here:
+# Additional details on configuring external access can be found here:
# https://atlassian.github.io/data-center-helm-charts/userguide/INSTALLATION/#4-configure-ingress
#
##
@@ -361,9 +362,9 @@ volumes:
# Ingress configuration
#
-# To make the Atlassian product available from outside the K8s cluster an Ingress
-# Controller should be pre-provisioned. With this in place the configuration below
-# can be used to configure an appropriate Ingress Resource.
+# Use this section when routing external traffic to Jira via a Kubernetes Ingress
+# resource (K8s Ingress API). Requires a pre-provisioned Ingress Controller.
+# If using the Gateway API instead, see the 'gateway' section below.
# https://atlassian.github.io/data-center-helm-charts/userguide/CONFIGURATION/#ingress
#
ingress:
@@ -373,6 +374,27 @@ ingress:
#
create: false
+ # -- The fully-qualified hostname (FQDN) of the Jira instance. This value is used
+ # to configure the product's proxy settings and, when ingress.create is true,
+ # the Ingress resource routing rules.
+ #
+ host:
+
+ # -- Whether users access the application over HTTPS. Set to 'false' if not
+ # using TLS, e.g. when reaching the service via localhost port-forwarding.
+ #
+ https: true
+
+ # -- The base path for the application, e.g. '/jira'.
+ # Defaults to 'jira.service.contextPath'.
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when ingress.create is true.
+ # They configure the Ingress resource itself and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
# -- Set to true if you want to create an OpenShift Route instead of an Ingress
#
openShiftRoute: false
@@ -424,29 +446,12 @@ ingress:
#
proxySendTimeout: 60
- # -- The fully-qualified hostname (FQDN) of the Ingress Resource. Traffic coming in on
- # this hostname will be routed by the Ingress Resource to the appropriate backend
- # Service.
- #
- host:
-
- # -- The base path for the Ingress Resource. For example '/jira'. Based on a
- # 'ingress.host' value of 'company.k8s.com' this would result in a URL of
- # 'company.k8s.com/jira'. Default value is 'jira.service.contextPath'
- #
- path:
-
# -- The custom annotations that should be applied to the Ingress Resource.
# If using an ingress-nginx controller be sure that the annotations you add
# here are compatible with those already defined in the 'ingess.yaml' template
#
annotations: {}
- # -- Set to 'true' if browser communication with the application should be TLS
- # (HTTPS) enforced.
- #
- https: true
-
# -- The name of the K8s Secret that contains the TLS private key and corresponding
# certificate. When utilised, TLS termination occurs at the ingress point where
# traffic to the Service, and it's Pods is in plaintext.
@@ -466,6 +471,120 @@ ingress:
# service: static-content-svc
# portNumber: 80
+# Gateway API configuration
+#
+# Use this section when routing external traffic to Jira via the Kubernetes
+# Gateway API, or when using an external proxy/load balancer without creating
+# any K8s routing resource. Only one of 'ingress' or 'gateway' should be used.
+# https://gateway-api.sigs.k8s.io/
+#
+gateway:
+
+ # -- Set to 'true' if an HTTPRoute Resource should be created. This depends on a
+ # pre-provisioned Gateway API controller being available and a Gateway resource.
+ # Cannot be enabled if ingress.create is true.
+ #
+ create: false
+
+ # -- The hostnames that should be routed to Jira. At least one hostname
+ # is required when gateway.create is true. Setting hostnames activates gateway
+ # mode for product configuration even when gateway.create is false, allowing
+ # use with a pre-existing Gateway or external proxy.
+ # The first entry is used as the canonical hostname for base URL, proxy
+ # settings, and NOTES output — list the primary/public hostname first.
+ #
+ hostnames: []
+ # - jira.example.com
+ # - jira-prod.example.com
+
+ # -- Whether users access the application over HTTPS.
+ # This does not configure TLS on the Gateway or load balancer — it must match
+ # how traffic is actually routed to the application.
+ #
+ https: true
+
+ # -- The port users connect on. Defaults to 443 (https) or 80 (http).
+ # This does not change the Gateway or load balancer port — it must match
+ # the port that is actually used. Only set for non-standard ports.
+ #
+ # externalPort:
+
+ # -- The base path for routing. When empty, falls back to the product's
+ # service.contextPath (same behavior as ingress). Set explicitly to
+ # override, e.g. "/jira".
+ #
+ path:
+
+ # ---------------------------------------------------------------------------
+ # The options below apply only when gateway.create is true.
+ # They configure the HTTPRoute resource and have no effect otherwise.
+ # ---------------------------------------------------------------------------
+
+ # -- Reference to the parent Gateway resource. Supports any standard
+ # parentRef fields (name, namespace, sectionName, etc.).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ #
+ parentRefs: []
+ # - name: example-gateway
+ # namespace: example-gateway-namespace
+ # sectionName: https
+
+ # -- Path matching type. Can be "PathPrefix", "Exact", or "RegularExpression".
+ # PathPrefix is recommended for most use cases.
+ #
+ pathType: "PathPrefix"
+
+ # -- Annotations to add to the HTTPRoute resource.
+ #
+ annotations: {}
+ # kubernetes.io/ingress.class: gateway
+ # cert-manager.io/cluster-issuer: letsencrypt
+
+ # -- Labels to add to the HTTPRoute resource.
+ #
+ labels: {}
+ # environment: production
+ # team: platform
+
+ # -- HTTP filters to apply to requests. Can be used to add/remove headers,
+ # perform redirects, or rewrite URLs.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteFilter
+ #
+ filters: []
+ # - type: RequestHeaderModifier
+ # requestHeaderModifier:
+ # add:
+ # - name: X-Forwarded-Proto
+ # value: https
+
+ # -- Advanced routing rules. Use this for complex routing scenarios like
+ # header-based routing, traffic splitting, or multiple backends.
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ #
+ additionalRules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api
+ # backendRefs:
+ # - name: jira-api
+ # port: 8080
+
+ # -- Session affinity (sticky sessions) is required for Atlassian DC products but
+ # is not part of the standard Gateway API HTTPRoute spec. It must be configured
+ # separately through your Gateway implementation's own policy resources.
+ # See docs/docs/examples/ingress/GATEWAY_API_SESSION_AFFINITY.md for working examples.
+
+ # -- Timeout configuration for HTTPRoute rules.
+ # Note: when migrating from Ingress, these replace proxyReadTimeout and
+ # proxySendTimeout. There is no Gateway API equivalent for
+ # proxyConnectTimeout or maxBodySize — those require controller-specific
+ # policies (e.g. Envoy Gateway BackendTrafficPolicy).
+ # See: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteTimeouts
+ #
+ timeouts:
+ request: "60s"
+ backendRequest: "60s"
# Jira configuration
#
@@ -839,13 +958,13 @@ jira:
protocol: "HTTP/1.1"
redirectPort: "8443"
acceptCount: "100"
- # secure is retrieved from ingress.https value
+ # secure is set based on the https setting (gateway.https or ingress.https)
secure:
- # scheme is set depending on ingress.https value (http if false, https if true)
+ # scheme is set based on the https setting (http if false, https if true)
scheme:
- # proxyName is retrieved from ingress.host value
+ # proxyName is set to the configured hostname (gateway.hostnames[0] or ingress.host)
proxyName:
- # proxyPort is set depending on ingress.https value (80 if http, 443 if https)
+ # proxyPort is set to the external port (defaults to 443 for https, 80 for http)
proxyPort:
maxHttpHeaderSize: "8192"
stuckThreadDetectionValveThreshold: "120"
diff --git a/src/test/config/kind/common-values.yaml b/src/test/config/kind/common-values.yaml
index 0ffa2853f..04877465f 100644
--- a/src/test/config/kind/common-values.yaml
+++ b/src/test/config/kind/common-values.yaml
@@ -108,15 +108,15 @@ podAnnotations:
quote: "true"
normal: annotation-comes-here
-# KinD is deployed with custom settings, namely extraPortMappings for ports 80 and 443
-# to make sure ingress traffic is available at http://localhost
-ingress:
+# KinD tests use Gateway API with Envoy Gateway.
+# Envoy proxy is exposed via NodePort(30080) -> hostPort(80); dc-app.test resolves via /etc/hosts.
+gateway:
create: true
- host: localhost
+ parentRefs:
+ - name: atlassian-gateway
+ hostnames:
+ - dc-app.test
https: false
- proxyConnectTimeout: 300
- proxyReadTimeout: 300
- proxySendTimeout: 300
monitoring:
exposeJmxMetrics: true
diff --git a/src/test/config/kind/envoy-proxy.yaml b/src/test/config/kind/envoy-proxy.yaml
new file mode 100644
index 000000000..8fe5a78a6
--- /dev/null
+++ b/src/test/config/kind/envoy-proxy.yaml
@@ -0,0 +1,30 @@
+# EnvoyProxy tells Envoy Gateway *how* to create the data-plane proxy
+# for any Gateway whose GatewayClass references this resource.
+#
+# Key behaviour:
+# - The proxy Service is created as NodePort (not the default ClusterIP/LB).
+# - A strategic-merge patch pins nodePort: 30080 for the HTTP listener,
+# matching the KinD extraPortMappings (host 80 → node 30080).
+#
+# This removes the need for any post-hoc `kubectl patch` in configure_kind.sh.
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+ name: kind-proxy-config
+ namespace: envoy-gateway-system
+spec:
+ provider:
+ type: Kubernetes
+ kubernetes:
+ envoyService:
+ type: NodePort
+ # Pin the HTTP listener to a fixed NodePort so it aligns with
+ # the KinD extraPortMappings entry (host :80 → node :30080).
+ patch:
+ type: StrategicMerge
+ value:
+ spec:
+ ports:
+ - port: 80
+ nodePort: 30080
+ protocol: TCP
diff --git a/src/test/config/kind/gateway.yaml b/src/test/config/kind/gateway.yaml
new file mode 100644
index 000000000..b1b32933d
--- /dev/null
+++ b/src/test/config/kind/gateway.yaml
@@ -0,0 +1,21 @@
+# Gateway API Gateway resource for testing
+# This Gateway is used by the KinD test environment to enable HTTPRoute testing
+# alongside traditional Ingress tests.
+#
+# The Gateway uses the Envoy Gateway controller (gatewayClassName: eg)
+# and listens on port 80 for HTTP traffic.
+#
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: atlassian-gateway
+ namespace: atlassian
+spec:
+ gatewayClassName: eg
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: Same
diff --git a/src/test/config/kind/kind-config.yml b/src/test/config/kind/kind-config.yml
index f714d6429..96f6490a4 100644
--- a/src/test/config/kind/kind-config.yml
+++ b/src/test/config/kind/kind-config.yml
@@ -2,29 +2,15 @@ kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
apiServerAddress: "127.0.0.1"
- apiServerPort: 6443
nodes:
- role: control-plane
- # this is required due to weird nginx controller behavior where it needs to redirect smth to
- # /dev/null but can't do it due to permission issues. The workaround is to mount /dev into control plane container
- extraMounts:
- - hostPath: /dev
- containerPath: /dev
- # the below config is required to properly install and use Nginx ingress
- # and use localhost to access DC applications
- kubeadmConfigPatches:
- - |
- kind: InitConfiguration
- nodeRegistration:
- kubeletExtraArgs:
- node-labels: "ingress-ready=true"
+ # Previously we mounted /dev + labelled ingress-ready to support ingress-nginx.
+ # KinD tests now run Gateway API via Envoy Gateway, exposed through a fixed NodePort.
extraPortMappings:
- - containerPort: 80
+ # Route host traffic to Envoy Gateway proxy via NodePort
+ - containerPort: 30080
hostPort: 80
protocol: TCP
- - containerPort: 443
- hostPort: 443
- protocol: TCP
# map registry NodePort to host port to be able to
# build and push images to an internal registry
- containerPort: 32767
diff --git a/src/test/config/openshift/envoy-proxy.yaml b/src/test/config/openshift/envoy-proxy.yaml
new file mode 100644
index 000000000..efecd7a21
--- /dev/null
+++ b/src/test/config/openshift/envoy-proxy.yaml
@@ -0,0 +1,19 @@
+# EnvoyProxy tells Envoy Gateway *how* to create the data-plane proxy
+# for any Gateway whose GatewayClass references this resource.
+#
+# On OpenShift/MicroShift we keep the default ClusterIP and expose
+# Envoy via an OpenShift Route (created in the workflow).
+# This resource exists so the GatewayClass can use a consistent
+# parametersRef pattern across environments.
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyProxy
+metadata:
+ name: openshift-proxy-config
+ namespace: envoy-gateway-system
+spec:
+ provider:
+ type: Kubernetes
+ kubernetes:
+ envoyService:
+ # ClusterIP is fine here — the OpenShift Route handles external access.
+ type: ClusterIP
diff --git a/src/test/config/openshift/gateway.yaml b/src/test/config/openshift/gateway.yaml
new file mode 100644
index 000000000..f902c3973
--- /dev/null
+++ b/src/test/config/openshift/gateway.yaml
@@ -0,0 +1,21 @@
+# Gateway API Gateway resource for OpenShift/MicroShift testing
+# This Gateway is used by the OpenShift test environment to enable HTTPRoute testing
+# alongside traditional Route tests.
+#
+# The Gateway uses a generic Gateway controller
+# and listens on port 80 for HTTP traffic.
+#
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: atlassian-gateway
+ namespace: atlassian
+spec:
+ gatewayClassName: eg
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: Same
diff --git a/src/test/config/openshift/openshift.yaml b/src/test/config/openshift/openshift.yaml
index 7a872add9..37c1c931a 100644
--- a/src/test/config/openshift/openshift.yaml
+++ b/src/test/config/openshift/openshift.yaml
@@ -12,6 +12,10 @@ volumes:
openshift:
runWithRestrictedSCC: true
-ingress:
- openShiftRoute: true
- host: atlassian.apps.crc.testing
+gateway:
+ create: true
+ parentRefs:
+ - name: atlassian-gateway
+ hostnames:
+ - atlassian.apps.crc.testing
+ https: false
diff --git a/src/test/java/test/GatewayTest.java b/src/test/java/test/GatewayTest.java
new file mode 100644
index 000000000..2ec4855d8
--- /dev/null
+++ b/src/test/java/test/GatewayTest.java
@@ -0,0 +1,293 @@
+package test;
+
+import org.assertj.core.api.AbstractStringAssert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.function.Executable;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import test.helm.Helm;
+import test.model.Kind;
+import test.model.Product;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static test.jackson.JsonNodeAssert.assertThat;
+
+class GatewayTest {
+ private Helm helm;
+
+ @BeforeEach
+ void initHelm(TestInfo testInfo) {
+ helm = new Helm(testInfo);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_creates_httproute_with_parent_ref_and_hostname(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoutes = resources.getAll(Kind.HTTPRoute);
+ assertEquals(1, httpRoutes.size());
+
+ final var httpRoute = httpRoutes.head();
+ assertThat(httpRoute.getNode("spec", "parentRefs").required(0).path("name"))
+ .hasTextEqualTo("my-gateway");
+ assertThat(httpRoute.getNode("spec", "hostnames").required(0))
+ .hasTextEqualTo(product + ".example.com");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_cannot_coexist_with_ingress(Product product) {
+ assertThrowsAssertionWithMessage(
+ "ERROR: Cannot enable both gateway.create and ingress.create",
+ () -> helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "ingress.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"))
+ );
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_requires_parentRefs_name(Product product) {
+ assertThrowsAssertionWithMessage(
+ "ERROR: gateway.parentRefs[0].name is required when gateway.create is true",
+ () -> helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].namespace", "gateway-system",
+ "gateway.hostnames[0]", product + ".example.com"))
+ );
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_requires_hostnames(Product product) {
+ assertThrowsAssertionWithMessage(
+ "ERROR: gateway.hostnames must contain at least one hostname when gateway.create is true",
+ () -> helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway"))
+ );
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_custom_path_is_applied_to_route_match(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.path", "/" + product));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("matches").required(0).path("path").path("value"))
+ .hasTextEqualTo("/" + product);
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_supports_multiple_hostnames(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.hostnames[1]", product + "-alt.example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "hostnames").required(0))
+ .hasTextEqualTo(product + ".example.com");
+ assertThat(httpRoute.getNode("spec", "hostnames").required(1))
+ .hasTextEqualTo(product + "-alt.example.com");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_namespace_is_set_on_parent_ref(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.parentRefs[0].namespace", "gateway-system",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "parentRefs").required(0).path("namespace"))
+ .hasTextEqualTo("gateway-system");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_supports_all_path_types(Product product) throws Exception {
+ for (String pathType : new String[]{"PathPrefix", "Exact", "RegularExpression"}) {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.pathType", pathType));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("matches").required(0).path("path").path("type"))
+ .hasTextEqualTo(pathType);
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_custom_annotations_are_applied(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.annotations.cert-manager\\.io/cluster-issuer", "letsencrypt"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("metadata", "annotations")
+ .path("cert-manager.io/cluster-issuer"))
+ .hasTextEqualTo("letsencrypt");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_custom_labels_are_applied(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.labels.environment", "production"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("metadata", "labels").path("environment"))
+ .hasTextEqualTo("production");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_backend_ref_targets_product_service(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ final var backendRef = httpRoute.getNode("spec", "rules").required(0)
+ .path("backendRefs").required(0);
+
+ assertThat(backendRef.path("name"))
+ .hasTextContaining(product.toString());
+ assertEquals(80, backendRef.path("port").asInt());
+ assertEquals(100, backendRef.path("weight").asInt());
+ }
+
+ @Test
+ void gateway_routes_synchrony_traffic_when_enabled() throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(Product.confluence, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", "confluence.example.com",
+ "synchrony.enabled", "true"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("backendRefs").required(0).path("name"))
+ .hasTextContaining("synchrony");
+
+ assertThat(httpRoute.getNode("spec", "rules").required(1)
+ .path("backendRefs").required(0).path("name"))
+ .hasTextContaining("confluence");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_custom_timeouts_are_applied(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com",
+ "gateway.timeouts.request", "120s",
+ "gateway.timeouts.backendRequest", "60s"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ final var rule = httpRoute.getNode("spec", "rules").required(0);
+ assertThat(rule.path("timeouts").path("request"))
+ .hasTextEqualTo("120s");
+ assertThat(rule.path("timeouts").path("backendRequest"))
+ .hasTextEqualTo("60s");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_has_default_timeouts(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ final var rule = httpRoute.getNode("spec", "rules").required(0);
+ assertThat(rule.path("timeouts").path("request"))
+ .hasTextEqualTo("60s");
+ assertThat(rule.path("timeouts").path("backendRequest"))
+ .hasTextEqualTo("60s");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent", "crowd"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_default_path_is_root(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("matches").required(0).path("path").path("value"))
+ .hasTextEqualTo("/");
+ }
+
+ @Test
+ void gateway_default_path_uses_context_path_for_crowd() throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(Product.crowd, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", "crowd.example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("matches").required(0).path("path").path("value"))
+ .hasTextEqualTo("/");
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = Product.class, names = {"bamboo_agent"}, mode = EnumSource.Mode.EXCLUDE)
+ void gateway_default_path_type_is_prefix(Product product) throws Exception {
+ final var resources = helm.captureKubeResourcesFromHelmChart(product, Map.of(
+ "gateway.create", "true",
+ "gateway.parentRefs[0].name", "my-gateway",
+ "gateway.hostnames[0]", product + ".example.com"));
+
+ final var httpRoute = resources.get(Kind.HTTPRoute);
+ assertThat(httpRoute.getNode("spec", "rules").required(0)
+ .path("matches").required(0).path("path").path("type"))
+ .hasTextEqualTo("PathPrefix");
+ }
+
+ private static void assertThrowsAssertionWithMessage(String expectedErrorMessage, Executable fn) {
+ assertThatString(
+ assertThrows(AssertionError.class, fn).getMessage()
+ ).contains(expectedErrorMessage);
+ }
+ private static AbstractStringAssert> assertThatString(String text) {
+ return org.assertj.core.api.Assertions.assertThat(text);
+ }
+}
diff --git a/src/test/java/test/IngressTest.java b/src/test/java/test/IngressTest.java
index 9585de4f4..93f586a5c 100644
--- a/src/test/java/test/IngressTest.java
+++ b/src/test/java/test/IngressTest.java
@@ -334,7 +334,7 @@ void bamboo_atl_base_path_when_no_ingress_path(Product product) throws Exception
"ingress.https", "true"));
resources.getStatefulSet(product.getHelmReleaseName()).getContainer().getEnv()
- .assertHasValue("ATL_BASE_URL", "https://myhost.mydomain");
+ .assertHasValue("ATL_BASE_URL", "https://myhost.mydomain/");
}
@ParameterizedTest
diff --git a/src/test/java/test/model/Kind.java b/src/test/java/test/model/Kind.java
index 1adbafe91..9fa46f788 100644
--- a/src/test/java/test/model/Kind.java
+++ b/src/test/java/test/model/Kind.java
@@ -4,5 +4,5 @@
* The different types of Kubernetes resource we use.
*/
public enum Kind {
- StatefulSet, Deployment, ServiceAccount, ConfigMap, Secret, Service, Pod, Job, ClusterRole, Role, ClusterRoleBinding, RoleBinding, PersistentVolume, PersistentVolumeClaim, Ingress, ServiceMonitor, PodDisruptionBudget
+ StatefulSet, Deployment, ServiceAccount, ConfigMap, Secret, Service, Pod, Job, ClusterRole, Role, ClusterRoleBinding, RoleBinding, PersistentVolume, PersistentVolumeClaim, Ingress, HTTPRoute, ServiceMonitor, PodDisruptionBudget
}
diff --git a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml
index 30eea7dd5..d4671320b 100644
--- a/src/test/resources/expected_helm_output/bamboo-agent/output.yaml
+++ b/src/test/resources/expected_helm_output/bamboo-agent/output.yaml
@@ -46,7 +46,6 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: dc046bb9cc091982418b9dbc914846069c346905db0b7ca4545a961dfa0e7f7b
labels:
app.kubernetes.io/name: bamboo-agent
app.kubernetes.io/instance: unittest-bamboo-agent
diff --git a/src/test/resources/expected_helm_output/bamboo/output.yaml b/src/test/resources/expected_helm_output/bamboo/output.yaml
index ef7c92a34..2114dbf9a 100644
--- a/src/test/resources/expected_helm_output/bamboo/output.yaml
+++ b/src/test/resources/expected_helm_output/bamboo/output.yaml
@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata:
name: unittest-bamboo
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -17,7 +17,7 @@ kind: ConfigMap
metadata:
name: unittest-bamboo-jvm-config
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -34,7 +34,7 @@ kind: ConfigMap
metadata:
name: unittest-bamboo-jmx-config
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -62,7 +62,7 @@ kind: ConfigMap
metadata:
name: unittest-bamboo-helm-values
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -251,6 +251,20 @@ data:
imageRepo: fluent/fluentd-kubernetes-daemonset
imageTag: v1.11.5-debian-elasticsearch7-1.2
resources: {}
+ gateway:
+ additionalRules: []
+ annotations: {}
+ create: false
+ filters: []
+ hostnames: []
+ https: true
+ labels: {}
+ parentRefs: []
+ path: null
+ pathType: PathPrefix
+ timeouts:
+ backendRequest: 60s
+ request: 60s
hostNamespaces: {}
image:
pullPolicy: IfNotPresent
@@ -371,7 +385,7 @@ kind: Service
metadata:
name: unittest-bamboo-jms
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -394,7 +408,7 @@ kind: Service
metadata:
name: unittest-bamboo-jmx
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -417,7 +431,7 @@ kind: Service
metadata:
name: unittest-bamboo
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -441,7 +455,7 @@ kind: StatefulSet
metadata:
name: unittest-bamboo
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -458,9 +472,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 6c2426b81340577644941f19819ed153ebb37ae2c3836fa6638a9e62c00085f9
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -627,7 +640,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
@@ -671,7 +684,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: bamboo-2.0.9
+ helm.sh/chart: bamboo-2.0.10
app.kubernetes.io/name: bamboo
app.kubernetes.io/instance: unittest-bamboo
app.kubernetes.io/version: "12.1.3"
diff --git a/src/test/resources/expected_helm_output/bitbucket/output.yaml b/src/test/resources/expected_helm_output/bitbucket/output.yaml
index bbb3c3a27..9449496aa 100644
--- a/src/test/resources/expected_helm_output/bitbucket/output.yaml
+++ b/src/test/resources/expected_helm_output/bitbucket/output.yaml
@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata:
name: unittest-bitbucket
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -17,7 +17,7 @@ kind: ConfigMap
metadata:
name: unittest-bitbucket-jvm-config-mesh
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -35,7 +35,7 @@ kind: ConfigMap
metadata:
name: unittest-bitbucket-jvm-config
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -54,7 +54,7 @@ kind: ConfigMap
metadata:
name: unittest-bitbucket-jmx-config
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -76,7 +76,7 @@ kind: ConfigMap
metadata:
name: unittest-bitbucket-helm-values
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -292,6 +292,20 @@ data:
imageRepo: fluent/fluentd-kubernetes-daemonset
imageTag: v1.11.5-debian-elasticsearch7-1.2
resources: {}
+ gateway:
+ additionalRules: []
+ annotations: {}
+ create: false
+ filters: []
+ hostnames: []
+ https: true
+ labels: {}
+ parentRefs: []
+ path: null
+ pathType: PathPrefix
+ timeouts:
+ backendRequest: 60s
+ request: 60s
hostNamespaces: {}
image:
pullPolicy: IfNotPresent
@@ -450,7 +464,7 @@ kind: Service
metadata:
name: unittest-bitbucket-jmx
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -476,7 +490,7 @@ kind: Service
metadata:
name: unittest-bitbucket-mesh-0
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -502,7 +516,7 @@ kind: Service
metadata:
name: unittest-bitbucket-mesh-1
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -528,7 +542,7 @@ kind: Service
metadata:
name: unittest-bitbucket-mesh-2
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -554,7 +568,7 @@ kind: Service
metadata:
name: unittest-bitbucket
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -586,7 +600,7 @@ kind: StatefulSet
metadata:
name: unittest-bitbucket-mesh
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -604,7 +618,6 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 5fd297317f6a4912733590b9173f61562d02dc369c01683e6f9def9b38955124
labels:
app.kubernetes.io/name: bitbucket-mesh
app.kubernetes.io/instance: unittest-bitbucket
@@ -707,7 +720,7 @@ kind: StatefulSet
metadata:
name: unittest-bitbucket
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -725,9 +738,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 213b5c2db2d1985012fc80d73db49125b74168c3ef2c96040ff532e546526c90
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -851,7 +863,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
@@ -883,7 +895,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: bitbucket-2.0.9
+ helm.sh/chart: bitbucket-2.0.10
app.kubernetes.io/name: bitbucket
app.kubernetes.io/instance: unittest-bitbucket
app.kubernetes.io/version: "10.2.1"
diff --git a/src/test/resources/expected_helm_output/confluence/output.yaml b/src/test/resources/expected_helm_output/confluence/output.yaml
index ef6897926..1b19f7d78 100644
--- a/src/test/resources/expected_helm_output/confluence/output.yaml
+++ b/src/test/resources/expected_helm_output/confluence/output.yaml
@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata:
name: unittest-confluence
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -17,7 +17,7 @@ kind: ConfigMap
metadata:
name: unittest-confluence-jvm-config
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -40,7 +40,7 @@ kind: ConfigMap
metadata:
name: unittest-confluence-jmx-config
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -62,7 +62,7 @@ kind: ConfigMap
metadata:
name: unittest-confluence-helm-values
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -243,6 +243,20 @@ data:
imageRepo: fluent/fluentd-kubernetes-daemonset
imageTag: v1.11.5-debian-elasticsearch7-1.2
resources: {}
+ gateway:
+ additionalRules: []
+ annotations: {}
+ create: false
+ filters: []
+ hostnames: []
+ https: true
+ labels: {}
+ parentRefs: []
+ path: null
+ pathType: PathPrefix
+ timeouts:
+ backendRequest: 60s
+ request: 60s
hostNamespaces: {}
image:
pullPolicy: IfNotPresent
@@ -490,7 +504,7 @@ kind: Service
metadata:
name: unittest-confluence-jmx
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -513,7 +527,7 @@ kind: Service
metadata:
name: unittest-confluence-synchrony
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence-synchrony
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -540,7 +554,7 @@ kind: Service
metadata:
name: unittest-confluence
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -568,7 +582,7 @@ kind: StatefulSet
metadata:
name: unittest-confluence-synchrony
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence-synchrony
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -585,9 +599,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 5c7e4f3183d49bd4e8c82a29b06246e551e4120042495652f1f9b27a0599a882
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence-synchrony
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -653,7 +666,7 @@ kind: StatefulSet
metadata:
name: unittest-confluence
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -670,9 +683,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 5fb6580d3d86adefce4a3cdf588b618930426168fab196e4059dffd36c1e17d2
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -807,7 +819,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
@@ -838,7 +850,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: confluence-2.0.9
+ helm.sh/chart: confluence-2.0.10
app.kubernetes.io/name: confluence
app.kubernetes.io/instance: unittest-confluence
app.kubernetes.io/version: "10.2.7"
diff --git a/src/test/resources/expected_helm_output/crowd/output.yaml b/src/test/resources/expected_helm_output/crowd/output.yaml
index b6327a5e9..74c58c5f2 100644
--- a/src/test/resources/expected_helm_output/crowd/output.yaml
+++ b/src/test/resources/expected_helm_output/crowd/output.yaml
@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata:
name: unittest-crowd
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -17,7 +17,7 @@ kind: ConfigMap
metadata:
name: unittest-crowd-jvm-config
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -36,7 +36,7 @@ kind: ConfigMap
metadata:
name: unittest-crowd-jmx-config
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -64,7 +64,7 @@ kind: ConfigMap
metadata:
name: unittest-crowd-helm-values
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -197,6 +197,20 @@ data:
imageRepo: fluent/fluentd-kubernetes-daemonset
imageTag: v1.11.5-debian-elasticsearch7-1.2
resources: {}
+ gateway:
+ additionalRules: []
+ annotations: {}
+ create: false
+ filters: []
+ hostnames: []
+ https: true
+ labels: {}
+ parentRefs: []
+ path: /
+ pathType: PathPrefix
+ timeouts:
+ backendRequest: 60s
+ request: 60s
hostNamespaces: {}
image:
pullPolicy: IfNotPresent
@@ -317,7 +331,7 @@ kind: Service
metadata:
name: unittest-crowd-jmx
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -340,7 +354,7 @@ kind: Service
metadata:
name: unittest-crowd
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -364,7 +378,7 @@ kind: StatefulSet
metadata:
name: unittest-crowd
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -381,9 +395,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 13f17fbad9620fd17fc56093ce0afa4b2320724668cbe4cc94b5a219379d3e62
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -517,7 +530,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
@@ -548,7 +561,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: crowd-2.0.9
+ helm.sh/chart: crowd-2.0.10
app.kubernetes.io/name: crowd
app.kubernetes.io/instance: unittest-crowd
app.kubernetes.io/version: "7.1.5"
diff --git a/src/test/resources/expected_helm_output/jira/output.yaml b/src/test/resources/expected_helm_output/jira/output.yaml
index 133a90b43..cbd240b64 100644
--- a/src/test/resources/expected_helm_output/jira/output.yaml
+++ b/src/test/resources/expected_helm_output/jira/output.yaml
@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata:
name: unittest-jira
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -17,7 +17,7 @@ kind: ConfigMap
metadata:
name: unittest-jira-jvm-config
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -36,7 +36,7 @@ kind: ConfigMap
metadata:
name: unittest-jira-jmx-config
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -62,7 +62,7 @@ kind: ConfigMap
metadata:
name: unittest-jira-helm-values
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -109,6 +109,20 @@ data:
imageRepo: fluent/fluentd-kubernetes-daemonset
imageTag: v1.11.5-debian-elasticsearch7-1.2
resources: {}
+ gateway:
+ additionalRules: []
+ annotations: {}
+ create: false
+ filters: []
+ hostnames: []
+ https: true
+ labels: {}
+ parentRefs: []
+ path: null
+ pathType: PathPrefix
+ timeouts:
+ backendRequest: 60s
+ request: 60s
hostNamespaces: {}
image:
pullPolicy: IfNotPresent
@@ -361,7 +375,7 @@ kind: Service
metadata:
name: unittest-jira-jmx
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -384,7 +398,7 @@ kind: Service
metadata:
name: unittest-jira
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -408,7 +422,7 @@ kind: StatefulSet
metadata:
name: unittest-jira
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -425,9 +439,8 @@ spec:
template:
metadata:
annotations:
- checksum/config-jvm: 5d632191c42871616de74827f73c9c72e383ec5c85f91e28f69c079802792851
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -562,7 +575,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
@@ -594,7 +607,7 @@ metadata:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": "before-hook-creation,hook-succeeded"
labels:
- helm.sh/chart: jira-2.0.9
+ helm.sh/chart: jira-2.0.10
app.kubernetes.io/name: jira
app.kubernetes.io/instance: unittest-jira
app.kubernetes.io/version: "11.3.3"
diff --git a/src/test/scripts/kind/configure_kind.sh b/src/test/scripts/kind/configure_kind.sh
index 6ebd75971..e986b22a6 100755
--- a/src/test/scripts/kind/configure_kind.sh
+++ b/src/test/scripts/kind/configure_kind.sh
@@ -6,19 +6,105 @@ kubectl cluster-info
echo "[INFO]: current-context:" $(kubectl config current-context)
echo "[INFO]: environment-kubeconfig:" "${KUBECONFIG}"
-kubectl create namespace atlassian
+kubectl create namespace atlassian --dry-run=client -o yaml | kubectl apply -f -
# even though there's a kind command to load a local image directly to KinD container runtime
# let's deploy an insecure registry in case we need it for any further tests
echo "[INFO]: Deploy ephemeral container registry"
kubectl apply -f src/test/config/kind/registry.yaml
-echo "[INFO]: Deploy Nginx ingress controller"
-kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
-kubectl wait --for=condition=ready pod \
- --selector=app.kubernetes.io/component=controller \
- --timeout=300s \
- -n ingress-nginx
+# Install Gateway API CRDs and Controller
+if [ -z "${SKIP_GATEWAY_API}" ]; then
+ echo "[INFO]: Installing Gateway API CRDs"
+ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
+ kubectl apply --server-side=true -f https://raw.githubusercontent.com/envoyproxy/gateway/v1.2.5/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml
+
+ echo "[INFO]: Waiting for Gateway API CRDs to be established"
+ kubectl wait --for condition=established --timeout=60s crd/gateways.gateway.networking.k8s.io
+ kubectl wait --for condition=established --timeout=60s crd/httproutes.gateway.networking.k8s.io
+ kubectl wait --for condition=established --timeout=60s crd/gatewayclasses.gateway.networking.k8s.io
+ kubectl wait --for condition=established --timeout=60s crd/envoyproxies.gateway.envoyproxy.io
+
+ echo "[INFO]: Installing Envoy Gateway"
+ # Envoy Gateway uses OCI registry, not a traditional Helm repo
+ helm install eg oci://docker.io/envoyproxy/gateway-helm \
+ --version v1.2.5 \
+ --create-namespace \
+ --namespace envoy-gateway-system \
+ --set deployment.envoyGateway.resources.requests.cpu=50m \
+ --set deployment.envoyGateway.resources.requests.memory=100Mi \
+ --skip-crds \
+ --timeout=300s \
+ --wait
+
+ echo "[INFO]: Waiting for Envoy Gateway to be ready"
+ kubectl wait --for=condition=available deployment/envoy-gateway \
+ --namespace envoy-gateway-system \
+ --timeout=300s
+
+ # EnvoyProxy CR tells the controller to create the data-plane proxy Service
+ # as NodePort with nodePort 30080 — matching the KinD extraPortMappings.
+ # This must be applied BEFORE the GatewayClass so the controller picks it up.
+ echo "[INFO]: Applying EnvoyProxy configuration (NodePort 30080)"
+ kubectl apply -f src/test/config/kind/envoy-proxy.yaml
+
+ # GatewayClass references the EnvoyProxy CR via parametersRef so every
+ # Gateway using this class gets the NodePort configuration automatically.
+ echo "[INFO]: Creating GatewayClass 'eg' with parametersRef"
+ cat << EOF | kubectl apply -f -
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+ name: eg
+spec:
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parametersRef:
+ group: gateway.envoyproxy.io
+ kind: EnvoyProxy
+ name: kind-proxy-config
+ namespace: envoy-gateway-system
+EOF
+
+ echo "[INFO]: Waiting for GatewayClass 'eg' to be accepted"
+ kubectl wait --for=condition=Accepted gatewayclass/eg --timeout=180s || {
+ echo "[ERROR]: GatewayClass not accepted in time"
+ kubectl get gatewayclass/eg -o yaml
+ exit 1
+ }
+
+ echo "[INFO]: Creating test Gateway resource in atlassian namespace"
+ kubectl apply -f src/test/config/kind/gateway.yaml
+
+ echo "[INFO]: Waiting for Gateway to be reconciled"
+ # In KinD there is no real LoadBalancer implementation, so the Gateway may never become
+ # fully Programmed (AddressNotAssigned). Instead, wait for:
+ # 1) Gateway Accepted=True (control-plane picked it up)
+ # 2) The Envoy proxy Deployment for this Gateway to become Available (data-plane ready)
+ kubectl wait --for=condition=Accepted gateway/atlassian-gateway -n atlassian --timeout=300s || {
+ echo "[ERROR]: Gateway not accepted in time"
+ kubectl describe gateway/atlassian-gateway -n atlassian
+ exit 1
+ }
+
+ echo "[INFO]: Waiting for Envoy proxy deployment for the Gateway"
+ kubectl wait --for=condition=Available deployment \
+ -n envoy-gateway-system \
+ -l gateway.envoyproxy.io/owning-gateway-name=atlassian-gateway \
+ --timeout=300s || {
+ echo "[ERROR]: Envoy proxy deployment not ready in time"
+ kubectl get deployments -n envoy-gateway-system -o wide
+ kubectl get pods -n envoy-gateway-system -o wide
+ exit 1
+ }
+
+ # Add /etc/hosts entry so dc-app.test resolves to localhost on the runner.
+ echo "[INFO]: Adding dc-app.test to /etc/hosts"
+ echo "127.0.0.1 dc-app.test" | sudo tee -a /etc/hosts
+
+ echo "[INFO]: Gateway API installation complete"
+else
+ echo "[INFO]: Skipping Gateway API installation (SKIP_GATEWAY_API is set)"
+fi
# this is for local runs, because existing nfs server images does not run on arm64 platforms
# instead, we create a hostPath RWX volume and override the default common settings
diff --git a/src/test/scripts/kind/deploy_app.sh b/src/test/scripts/kind/deploy_app.sh
index 4de15d4a2..8e825c993 100755
--- a/src/test/scripts/kind/deploy_app.sh
+++ b/src/test/scripts/kind/deploy_app.sh
@@ -1,5 +1,32 @@
#!/usr/bin/env bash
+# Poll until a command succeeds or timeout is reached.
+# Usage: wait_for "description" timeout_seconds interval_seconds command [args...]
+wait_for() {
+ local desc="$1"; shift
+ local timeout="$1"; shift
+ local interval="$1"; shift
+ local elapsed=0
+
+ while ! "$@" >/dev/null 2>&1; do
+ if [ $elapsed -ge $timeout ]; then
+ echo "[ERROR]: Timed out after ${elapsed}s waiting for: ${desc}"
+ return 1
+ fi
+ echo "[INFO]: Waiting for ${desc}... (${elapsed}/${timeout}s)"
+ sleep $interval
+ elapsed=$((elapsed + interval))
+ done
+ echo "[INFO]: ${desc} — ready"
+}
+
+# Check if a URL returns HTTP 200.
+# Usage: check_http_200 url [extra-curl-args...]
+check_http_200() {
+ local url="$1"; shift
+ test "$(curl -s -o /dev/null -w '%{http_code}' "$@" "$url")" = "200"
+}
+
# Deploy CloudNativePG operator and PostgreSQL cluster
deploy_postgres() {
echo "[INFO]: Installing CloudNativePG operator"
@@ -47,36 +74,29 @@ deploy_postgres() {
# Wait for operator to be ready
echo "[INFO]: Waiting for CloudNativePG operator to be ready"
- for i in {1..60}; do
- if kubectl get crd clusters.postgresql.cnpg.io >/dev/null 2>&1; then
- echo "[INFO]: CloudNativePG CRDs are available"
- break
- fi
- echo "[INFO]: Waiting for CloudNativePG CRDs to be available... ($i/60)"
- sleep 5
- done
-
+ wait_for "CloudNativePG CRDs" 300 5 kubectl get crd clusters.postgresql.cnpg.io || {
+ echo "[ERROR]: CloudNativePG CRDs not available"
+ exit 1
+ }
# Wait for operator deployment to exist
echo "[INFO]: Waiting for operator deployment to be created..."
- for i in {1..30}; do
- if kubectl get deployment -n cnpg-system -l app.kubernetes.io/name=cloudnative-pg >/dev/null 2>&1; then
- echo "[INFO]: Operator deployment found"
- # MicroShift/OpenShift: grant anyuid SCC to operator SA to satisfy UID range constraints
- if kubectl api-resources | grep -q "securitycontextconstraints"; then
- echo "[INFO]: Detected SCC support; granting 'anyuid' SCC to operator service account"
- SA_NAME="cnpg-operator-cloudnative-pg"
- # Try with oc if available, otherwise patch SCC directly
- if command -v oc >/dev/null 2>&1; then
- oc adm policy add-scc-to-user anyuid -z "$SA_NAME" -n cnpg-system || true
- else
- kubectl patch scc anyuid --type=json -p='[{"op":"add","path":"/users/-","value":"system:serviceaccount:cnpg-system:'"$SA_NAME"'"}]' || true
- fi
- fi
- break
+ wait_for "CloudNativePG operator deployment" 60 2 \
+ kubectl get deployment -n cnpg-system -l app.kubernetes.io/name=cloudnative-pg || {
+ echo "[WARNING]: CloudNativePG operator deployment not found"
+ # this will be checked again below - keeping old behavior to minimize risk of this change
+ }
+
+ # MicroShift/OpenShift: grant anyuid SCC to operator SA to satisfy UID range constraints
+ if kubectl api-resources | grep -q "securitycontextconstraints"; then
+ echo "[INFO]: Detected SCC support; granting 'anyuid' SCC to operator service account"
+ SA_NAME="cnpg-operator-cloudnative-pg"
+ # Try with oc if available, otherwise patch SCC directly
+ if command -v oc >/dev/null 2>&1; then
+ oc adm policy add-scc-to-user anyuid -z "$SA_NAME" -n cnpg-system || true
+ else
+ kubectl patch scc anyuid --type=json -p='[{"op":"add","path":"/users/-","value":"system:serviceaccount:cnpg-system:'"$SA_NAME"'"}]' || true
fi
- echo "[INFO]: Waiting for operator deployment... ($i/30)"
- sleep 2
- done
+ fi
# Verify operator is actually running
echo "[DEBUG]: CloudNativePG operator deployments:"
@@ -339,12 +359,17 @@ deploy_app() {
fi
}
-# Resolve the ingress/gateway hostname based on the deployment environment.
+# Resolve the routing hostname based on the deployment environment.
+#
+# - KinD: Envoy Gateway proxy is exposed via NodePort(30080) -> hostPort(80)
+# and the workflow adds /etc/hosts so dc-app.test resolves locally.
+# - OpenShift/MicroShift: Envoy proxy is exposed via an OpenShift Route on the
+# CRC apps domain.
get_routing_hostname() {
if [ -n "${OPENSHIFT_VALUES}" ]; then
echo "atlassian.apps.crc.testing"
else
- echo "localhost"
+ echo "dc-app.test"
fi
}
@@ -356,17 +381,17 @@ get_routing_hostname() {
# during setup triggers the wizard to advance to the next step.
wait_for_bamboo_setup() {
echo "[INFO]: Waiting for Bamboo server unattended setup to complete..."
+
SETUP_HOSTNAME=$(get_routing_hostname)
SETUP_TIMEOUT=300
SETUP_ELAPSED=0
while [ ${SETUP_ELAPSED} -lt ${SETUP_TIMEOUT} ]; do
- RESPONSE=$(curl -s -o /dev/null -w "%{http_code}|%{redirect_url}" --max-redirs 0 http://${SETUP_HOSTNAME}/ 2>/dev/null || echo "000|")
+ RESPONSE=$(curl -s -o /dev/null -w "%{http_code}|%{redirect_url}" --max-redirs 0 "http://${SETUP_HOSTNAME}/" 2>/dev/null || echo "000|")
HTTP_CODE=$(echo "$RESPONSE" | cut -d'|' -f1)
REDIRECT_URL=$(echo "$RESPONSE" | cut -d'|' -f2)
if echo "${REDIRECT_URL}" | grep -q "bootstrap\|setup"; then
- # Server is in setup mode — trigger setup advancement by following the redirect chain
- curl -s -o /dev/null -L --max-redirs 10 http://${SETUP_HOSTNAME}/ 2>/dev/null || true
+ curl -s -o /dev/null -L --max-redirs 10 "http://${SETUP_HOSTNAME}/" 2>/dev/null || true
echo "[INFO]: Bamboo setup in progress (HTTP ${HTTP_CODE} → ${REDIRECT_URL}). Triggering setup... (${SETUP_ELAPSED}s/${SETUP_TIMEOUT}s)"
elif [ "${HTTP_CODE}" = "302" ] && echo "${REDIRECT_URL}" | grep -q "userlogin"; then
echo "[INFO]: Bamboo server setup complete (redirecting to login page)"
@@ -382,37 +407,38 @@ wait_for_bamboo_setup() {
done
echo "[WARNING]: Bamboo setup did not complete within ${SETUP_TIMEOUT}s. Proceeding with agent deployment anyway."
- echo "[DEBUG]: Full response from GET / with redirects:"
- curl -v -s -L --max-redirs 10 http://${SETUP_HOSTNAME}/ 2>&1 || true
+ curl -v -s -L --max-redirs 10 "http://${SETUP_HOSTNAME}/" 2>&1 || true
}
-verify_ingress() {
+verify_gateway_ingress() {
STATUS_ENDPOINT_PATH="status"
if [ ${DC_APP} == "bamboo" ]; then
STATUS_ENDPOINT_PATH="rest/api/latest/status"
elif [ ${DC_APP} == "crowd" ]; then
STATUS_ENDPOINT_PATH="crowd/status"
fi
- echo "[INFO]: Checking ${DC_APP} status"
- # give ingress controller a few seconds before polling
- sleep 5
+
+ echo "[INFO]: Checking ${DC_APP} status via Gateway API"
+
+ wait_for "HTTPRoute ${DC_APP}" 60 2 kubectl get httproute/${DC_APP} -n atlassian || {
+ echo "[ERROR]: HTTPRoute ${DC_APP} not found in atlassian namespace"
+ kubectl get httproute -n atlassian || true
+ exit 1
+ }
+
HOSTNAME=$(get_routing_hostname)
- for i in {1..10}; do
- STATUS=$(curl -s -o /dev/null -w '%{http_code}' http://${HOSTNAME}/${STATUS_ENDPOINT_PATH})
- if [ $STATUS -ne 200 ]; then
- echo "[ERROR]: Status code is not 200. Waiting 10 seconds"
- sleep 10
- else
- echo "[INFO]: Received status ${STATUS}"
- curl -s http://${HOSTNAME}/${STATUS_ENDPOINT_PATH}
- echo -e "\n"
- break
- fi
- done
- if [ $STATUS -ne 200 ]; then
- curl -v http://${HOSTNAME}/${STATUS_ENDPOINT_PATH}
- exit 1
- fi
+ STATUS_URL="http://${HOSTNAME}/${STATUS_ENDPOINT_PATH}"
+
+ wait_for "${DC_APP} HTTP 200 via Gateway" 120 10 \
+ check_http_200 "${STATUS_URL}" || {
+ echo "[ERROR]: ${DC_APP} did not return HTTP 200 via Gateway"
+ curl -v "${STATUS_URL}"
+ exit 1
+ }
+
+ echo "[INFO]: ${DC_APP} responded successfully via Gateway"
+ curl -s "${STATUS_URL}"
+ echo -e "\n"
}
verify_metrics() {
@@ -479,6 +505,73 @@ verify_openshift_analytics() {
fi
}
+verify_gateway() {
+ echo "[INFO]: Verifying HTTPRoute resource for ${DC_APP}"
+ if ! kubectl get httproute/${DC_APP} -n atlassian >/dev/null 2>&1; then
+ echo "[ERROR]: HTTPRoute ${DC_APP} not found in atlassian namespace"
+ kubectl get httproute -n atlassian || true
+ exit 1
+ fi
+
+ echo "[INFO]: Checking HTTPRoute status"
+
+ # Wait on Gateway API parent conditions (Envoy Gateway reports conditions under
+ # `.status.parents[*].conditions`). Use a JSONPath filter by type.
+ # Note: Don't escape the double-quotes inside the single-quoted JSONPath.
+
+ kubectl wait \
+ --for=jsonpath='{.status.parents[0].conditions[?(@.type=="Accepted")].status}'=True \
+ httproute/${DC_APP} -n atlassian --timeout=180s || {
+ echo "[ERROR]: HTTPRoute not accepted"
+ echo "[DEBUG]: HTTPRoute status (YAML)"
+ kubectl get httproute/${DC_APP} -n atlassian -o yaml | sed -n '/^status:/,$p' || true
+ echo "[DEBUG]: HTTPRoute status.parents (JSON)"
+ kubectl get httproute/${DC_APP} -n atlassian -o json | jq '.status.parents' || true
+ echo "[DEBUG]: Gateway status (YAML)"
+ kubectl get gateway/atlassian-gateway -n atlassian -o yaml | sed -n '/^status:/,$p' || true
+ echo "[DEBUG]: Envoy Gateway deployments/pods"
+ kubectl get deployments -n envoy-gateway-system -o wide || true
+ kubectl get pods -n envoy-gateway-system -o wide || true
+ kubectl describe httproute/${DC_APP} -n atlassian
+ exit 1
+ }
+
+ kubectl wait \
+ --for=jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")].status}'=True \
+ httproute/${DC_APP} -n atlassian --timeout=180s || {
+ echo "[ERROR]: HTTPRoute ResolvedRefs condition not met"
+ echo "[DEBUG]: HTTPRoute status.parents (JSON)"
+ kubectl get httproute/${DC_APP} -n atlassian -o json | jq '.status.parents' || true
+ kubectl describe httproute/${DC_APP} -n atlassian
+ exit 1
+ }
+
+ echo "[INFO]: HTTPRoute is Accepted and ResolvedRefs verified"
+
+ echo "[INFO]: Verifying Gateway attachment"
+ GATEWAY_NAME=$(kubectl get httproute/${DC_APP} -n atlassian -o jsonpath='{.spec.parentRefs[0].name}')
+ echo "[INFO]: HTTPRoute attached to Gateway: ${GATEWAY_NAME}"
+
+ if [ -z "${GATEWAY_NAME}" ]; then
+ echo "[ERROR]: No Gateway referenced in HTTPRoute"
+ exit 1
+ fi
+
+ echo "[INFO]: Checking Gateway status"
+ kubectl get gateway/${GATEWAY_NAME} -n atlassian -o yaml
+
+ # Verify hostnames are configured
+ HOSTNAMES=$(kubectl get httproute/${DC_APP} -n atlassian -o jsonpath='{.spec.hostnames[*]}')
+ echo "[INFO]: HTTPRoute hostnames: ${HOSTNAMES}"
+
+ if [ -z "${HOSTNAMES}" ]; then
+ echo "[ERROR]: No hostnames configured on HTTPRoute"
+ exit 1
+ fi
+
+ echo "[INFO]: Gateway API verification complete for ${DC_APP}"
+}
+
# create 2 NodePort services to expose each DC pod, required for functional tests
# where communication between nodes and cache replication is tested
create_backdoor_services() {