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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/atlantis/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: v1
appVersion: v0.36.0
description: A Helm chart for Atlantis https://www.runatlantis.io
name: atlantis
version: 5.19.0
version: 5.20.0
keywords:
- terraform
home: https://www.runatlantis.io
Expand Down
54 changes: 54 additions & 0 deletions charts/atlantis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,57 @@ extraManifests:
name: "gcp-cloud-armor-policy-test"
```

## High Availability and Sticky Sessions

When running multiple replicas, enable sticky sessions to keep the browser session (including WebSocket upgrades) routed to the same Atlantis pod.

- Service-level stickiness (ClientIP):

```yaml
service:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
```

- If using NGINX Ingress, consider cookie-based affinity and longer WS timeouts:

```yaml
ingress:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
```

- If using Gateway API, you can enable cookie persistence via an extra manifest (controller support required):

```yaml
extraManifests:
- apiVersion: gateway.networking.x-k8s.io/v1alpha1
kind: XBackendTrafficPolicy
metadata:
name: atlantis-session-persistence
namespace: ".Release.Namespace"
spec:
targetRefs:
- group: ""
kind: Service
name: atlantis
sessionPersistence:
type: Cookie
sessionName: atlantis-session
idleTimeout: 1h
absoluteTimeout: 24h
cookieConfig:
lifetimeType: Session
```

Optionally, set `service.internalTrafficPolicy: Local` or `Cluster` depending on your environment and how you want internal routing handled.

## Values

| Key | Type | Default | Description |
Expand Down Expand Up @@ -186,11 +237,14 @@ extraManifests:
| secret.annotations | object | `{}` | Annotations for the Secrets. Check values.yaml for examples. |
| service.annotations | object | `{}` | |
| service.externalTrafficPolicy | string | `nil` | |
| service.internalTrafficPolicy | string | `nil` | [optional] Internal traffic policy for the Service. One of: Cluster, Local. |
| service.loadBalancerIP | string | `nil` | |
| service.loadBalancerSourceRanges | list | `[]` | |
| service.nodePort | string | `nil` | |
| service.port | int | `80` | |
| service.portName | string | `"atlantis"` | |
| service.sessionAffinity | string | `nil` | [optional] Kubernetes Service sessionAffinity setting. One of: ClientIP, None. |
| service.sessionAffinityConfig | object | `nil` | [optional] Kubernetes Service sessionAffinityConfig. Only applicable when sessionAffinity=ClientIP. |
| service.targetPort | int | `4141` | [optional] Define the port you would like atlantis to run on. Defaults to 4141. |
| service.type | string | `"NodePort"` | |
| serviceAccount.annotations | object | `{}` | Annotations for the Service Account. Check values.yaml for examples. |
Expand Down
51 changes: 51 additions & 0 deletions charts/atlantis/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,57 @@ extraManifests:
name: "gcp-cloud-armor-policy-test"
```

## High Availability and Sticky Sessions

When running multiple replicas, enable sticky sessions to keep the browser session (including WebSocket upgrades) routed to the same Atlantis pod.

- Service-level stickiness (ClientIP):

```yaml
service:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
```

- If using NGINX Ingress, consider cookie-based affinity and longer WS timeouts:

```yaml
ingress:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
```

- If using Gateway API, you can enable cookie persistence via an extra manifest (controller support required):

```yaml
extraManifests:
- apiVersion: gateway.networking.x-k8s.io/v1alpha1
kind: XBackendTrafficPolicy
metadata:
name: atlantis-session-persistence
namespace: "{{ `.Release.Namespace` }}"
spec:
targetRefs:
- group: ""
kind: Service
name: atlantis
sessionPersistence:
type: Cookie
sessionName: atlantis-session
idleTimeout: 1h
absoluteTimeout: 24h
cookieConfig:
lifetimeType: Session
```

Optionally, set `service.internalTrafficPolicy: Local` or `Cluster` depending on your environment and how you want internal routing handled.

{{ template "chart.valuesSection" . }}

## Upgrading
Expand Down
10 changes: 10 additions & 0 deletions charts/atlantis/templates/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ spec:
{{- if .Values.service.externalTrafficPolicy }}
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
{{- end }}
{{- if .Values.service.internalTrafficPolicy }}
internalTrafficPolicy: {{ .Values.service.internalTrafficPolicy }}
{{- end }}
{{- if .Values.service.sessionAffinity }}
sessionAffinity: {{ .Values.service.sessionAffinity }}
{{- end }}
{{- if .Values.service.sessionAffinityConfig }}
sessionAffinityConfig:
{{- toYaml .Values.service.sessionAffinityConfig | nindent 4 }}
{{- end }}
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
Expand Down
39 changes: 39 additions & 0 deletions charts/atlantis/tests/service_affinity_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
suite: test service session affinity
templates:
- templates/service.yaml

# Validate that new Service fields render only when configured and match values

tests:
- it: does not render affinity or internalTrafficPolicy by default
asserts:
- notExists:
path: spec.sessionAffinity
- notExists:
path: spec.sessionAffinityConfig
- notExists:
path: spec.internalTrafficPolicy

- it: renders sessionAffinity ClientIP and sessionAffinityConfig when set
set:
service:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
asserts:
- equal:
path: spec.sessionAffinity
value: ClientIP
- equal:
path: spec.sessionAffinityConfig.clientIP.timeoutSeconds
value: 10800

- it: renders internalTrafficPolicy when set
set:
service:
internalTrafficPolicy: Local
asserts:
- equal:
path: spec.internalTrafficPolicy
value: Local
45 changes: 45 additions & 0 deletions charts/atlantis/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,51 @@
"null"
]
},
"internalTrafficPolicy": {
"description": "internalTrafficPolicy to set on service.",
"type": [
"string",
"null"
],
"enum": [
null,
"Cluster",
"Local"
]
},
"sessionAffinity": {
"description": "Kubernetes Service sessionAffinity. Set to ClientIP to enable stickiness.",
"type": [
"string",
"null"
],
"enum": [
null,
"ClientIP",
"None"
]
},
"sessionAffinityConfig": {
"description": "Kubernetes Service sessionAffinityConfig. Only applicable when sessionAffinity=ClientIP.",
"type": [
"object",
"null"
],
"additionalProperties": false,
"properties": {
"clientIP": {
"type": "object",
"additionalProperties": false,
"properties": {
"timeoutSeconds": {
"type": "integer",
"minimum": 1,
"description": "Specifies the seconds of ClientIP session sticky time."
}
}
}
}
},
"loadBalancerIP": {
"description": "IP address to assign to load balancer (if supported).",
"type": [
Expand Down
6 changes: 6 additions & 0 deletions charts/atlantis/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ service:
loadBalancerIP: null
loadBalancerSourceRanges: []
externalTrafficPolicy: null
# -- (string) [optional] Internal traffic policy for the Service. One of: Cluster, Local.
internalTrafficPolicy: null
# -- (string) [optional] Kubernetes Service sessionAffinity setting. One of: ClientIP, None.
sessionAffinity: null
# -- (object) [optional] Kubernetes Service sessionAffinityConfig. Only applicable when sessionAffinity=ClientIP.
sessionAffinityConfig: null

podTemplate:
# -- Check values.yaml for examples.
Expand Down