Skip to content
Merged
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/core/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ apiVersion: v2
name: charts-core
description: A Helm chart for Kubernetes
type: application
version: 2.7.0
version: 2.8.0
27 changes: 25 additions & 2 deletions charts/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This chart supports integration with External Secrets Operator for syncing secre
### Prerequisites

ESO requires the following secrets to be defined in `secEnvVars`:

- `AZURE_CLIENT_ID` - Azure Service Principal Client ID
- `AZURE_CLIENT_SECRET` - Azure Service Principal Client Secret
- `AZURE_TENANT_ID` - Azure Tenant ID
Expand Down Expand Up @@ -34,17 +35,39 @@ global:
### Generated Resources

When ESO is enabled, the chart creates:

- **ClusterSecretStore**: `<release-name>-cluster-secret-store` - connects to Azure Key Vault
- **ExternalSecret**: `<release-name>-external-secret` - syncs secrets to `<release-name>-secure-kv`

The ESO will use the existing `<release-name>-secure` secret (created from `secEnvVars`) for authentication to Azure Key Vault.

# How to test locally
## Sticky Sessions

Enables cookie-based session affinity for Traefik IngressRoutes. **Mutually exclusive with canary deployments** — when `global.canary.enabled` is `true`, sticky config is ignored. Flagger officially doesn't support sticky sessions, as well as it breaks user weighted routing when rollout is happening.

### Configuration

```yaml
global:
ingressRoutes:
routes:
- ruleName: private
sticky:
enabled: true
name: my-cookie # default: <release-name>-sticky
secure: false # default: true
httpOnly: true # default: true
sameSite: strict # default: strict
```

## How to test locally

1. Install prerequisites as specified in tests requirements.txt
2. in charts\charts\core type "helm template ." make sure the template renders correctly
3. in charts\charts\core type "pytest" all tests should pass

# How to debug in VS code
## How to debug in VS code

https://code.visualstudio.com/docs/python/testing
test discovery in subfolders is based on existence of __init__.py file
to run tests succesfully you need to set test working directory go to File->Preferences->settings, search Tests, select Python and find "Optional working directory for tests." Set it to charts\core
8 changes: 8 additions & 0 deletions charts/core/templates/CRD/ingressroute.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ spec:
name: {{ $rangeItem.serviceName | default ( include "charts-core.fullname" . ) }}
namespace: {{ $rangeItem.serviceNamespace | default .Release.Namespace | quote}}
port: {{ $rangeItem.servicePort | default 80 }}
{{- if and (dig "sticky" "enabled" false $rangeItem) (not .Values.global.canary.enabled) }}
sticky:
cookie:
name: {{ $rangeItem.sticky.name | default (printf "%s-sticky" (include "charts-core.fullname" .)) }}
secure: {{ if kindIs "invalid" $rangeItem.sticky.secure }}true{{ else }}{{ $rangeItem.sticky.secure }} {{ end }}
httpOnly: {{ if kindIs "invalid" $rangeItem.sticky.httpOnly }}true{{ else }}{{ $rangeItem.sticky.httpOnly }} {{ end }}
sameSite: {{ $rangeItem.sticky.sameSite | default "strict" }}
{{- end }}
{{- if or $rangeItem.isStripprefixEnabled $rangeItem.stripPrefixes (ne "false" ($rangeItem.isRetryEnabled | toString)) $rangeItem.isCircuitBreakerEnabled }}
middlewares:
{{- end }}
Expand Down
231 changes: 231 additions & 0 deletions charts/core/templates/tests/ingressroute_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,235 @@ def test_host_rule_and_circuitbreaker_middleware_configured(self):
"NetworkErrorRatio() > 0.10",
jmespath.search(
"spec.circuitBreaker.expression", docs[1])
)

def test_sticky_session_rendered_when_enabled(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertIsNotNone(jmespath.search(
"spec.routes[0].services[0].sticky.cookie", docs[0]))

def test_sticky_session_not_rendered_when_disabled(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": False
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertIsNone(jmespath.search(
"spec.routes[0].services[0].sticky", docs[0]))

def test_sticky_session_not_rendered_when_canary_enabled(self):
docs = render_chart(
values={
"global": {
"canary": {
"enabled": True
},
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertIsNone(jmespath.search(
"spec.routes[0].services[0].sticky", docs[0]))

def test_sticky_session_custom_cookie_name(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True,
"name": "my-cookie"
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertEqual(
"my-cookie",
jmespath.search(
"spec.routes[0].services[0].sticky.cookie.name", docs[0])
)

def test_sticky_session_secure_defaults_to_true(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertTrue(jmespath.search(
"spec.routes[0].services[0].sticky.cookie.secure", docs[0]))

def test_sticky_session_http_only_defaults_to_true(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertTrue(jmespath.search(
"spec.routes[0].services[0].sticky.cookie.httpOnly", docs[0]))

def test_sticky_session_http_only_false_is_respected(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True,
"httpOnly": False
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertFalse(jmespath.search(
"spec.routes[0].services[0].sticky.cookie.httpOnly", docs[0]))

def test_sticky_session_same_site_defaults_to_strict(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertEqual(
"strict",
jmespath.search(
"spec.routes[0].services[0].sticky.cookie.sameSite", docs[0])
)

def test_sticky_session_custom_same_site(self):
docs = render_chart(
values={
"global": {
"ingressRoutes": {
"enabled": True,
"routes": [
{
"ruleName": "http",
"isRetryEnabled": False,
"sticky": {
"enabled": True,
"sameSite": "lax"
}
}
]
}
}
},
name=".",
show_only=["templates/CRD/ingressroute.yaml"]
)
self.assertEqual(
"lax",
jmespath.search(
"spec.routes[0].services[0].sticky.cookie.sameSite", docs[0])
)
6 changes: 6 additions & 0 deletions charts/core/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ global:
# - /vadisservice
#isRetryEnabled: true
#isCircuitBreakerEnabled: true
#sticky: # Preview feature. Currently works only with canary deployments disabled.
#enabled: false
#name: cookie-name
#secure: true
#httpOnly: true
#sameSite: strict

envVarsEnabled: true
envVars:
Expand Down
Loading