Compare commits
23 commits
main
...
feat/redis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b222514066 | ||
|
|
e9e74a112c | ||
|
|
8048868c6e | ||
|
|
7cc892163b | ||
|
|
e47e3103fb | ||
|
|
61b8410321 | ||
|
|
125bd116ec | ||
|
|
a8fc515649 | ||
|
|
94663e3094 | ||
|
|
08d2b065e1 | ||
|
|
47936637b6 | ||
|
|
9102d8094a | ||
|
|
a18bfc3f91 | ||
|
|
857ebd0420 | ||
|
|
d482d1e56a | ||
|
|
3dfde08242 | ||
|
|
cdc0e21831 | ||
|
|
6dca3f52b6 | ||
|
|
ab6d7ad6d5 | ||
|
|
2aa4863cdd | ||
|
|
2e1fc38405 | ||
|
|
cb932d17f4 | ||
|
|
325132d48e |
31 changed files with 1381 additions and 5 deletions
|
|
@ -41,6 +41,9 @@ type ConfigSpec struct {
|
|||
// Enable password generation.
|
||||
// +kubebuilder:default:=true
|
||||
AuthEnabled bool `json:"authEnabled"`
|
||||
// TLS configuration for client-facing connections. When enabled, an nginx stream sidecar terminates TLS on port 6380. Intra-cluster traffic on 6379 (operator probes, sentinel, replication) remains plaintext.
|
||||
// +kubebuilder:default:={}
|
||||
Tls TLS `json:"tls"`
|
||||
}
|
||||
|
||||
type Resources struct {
|
||||
|
|
@ -50,6 +53,27 @@ type Resources struct {
|
|||
Memory resource.Quantity `json:"memory,omitempty"`
|
||||
}
|
||||
|
||||
type TLS struct {
|
||||
// Enable TLS termination via nginx stream sidecar.
|
||||
// +kubebuilder:default:=false
|
||||
Enabled bool `json:"enabled"`
|
||||
// cert-manager Issuer reference. Used only when secretName is empty.
|
||||
// +kubebuilder:default:={}
|
||||
IssuerRef TLSIssuerRef `json:"issuerRef"`
|
||||
// Name of an existing kubernetes.io/tls secret. When non-empty, cert-manager Certificate is NOT rendered and the user manages the secret.
|
||||
// +kubebuilder:default:=""
|
||||
SecretName string `json:"secretName"`
|
||||
}
|
||||
|
||||
type TLSIssuerRef struct {
|
||||
// Either "Issuer" or "ClusterIssuer".
|
||||
// +kubebuilder:default:="ClusterIssuer"
|
||||
Kind string `json:"kind"`
|
||||
// Issuer/ClusterIssuer resource name.
|
||||
// +kubebuilder:default:="selfsigned-cluster-issuer"
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum="nano";"micro";"small";"medium";"large";"xlarge";"2xlarge"
|
||||
type ResourcesPreset string
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) {
|
|||
*out = *in
|
||||
in.Resources.DeepCopyInto(&out.Resources)
|
||||
out.Size = in.Size.DeepCopy()
|
||||
out.Tls = in.Tls
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigSpec.
|
||||
|
|
@ -83,3 +84,34 @@ func (in *Resources) DeepCopy() *Resources {
|
|||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLS) DeepCopyInto(out *TLS) {
|
||||
*out = *in
|
||||
out.IssuerRef = in.IssuerRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS.
|
||||
func (in *TLS) DeepCopy() *TLS {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TLS)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLSIssuerRef) DeepCopyInto(out *TLSIssuerRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSIssuerRef.
|
||||
func (in *TLSIssuerRef) DeepCopy() *TLSIssuerRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TLSIssuerRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,15 @@ Service utilizes the Spotahome Redis Operator for efficient management and orche
|
|||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ------------- | --------------------------- | ------ | ------ |
|
||||
| `authEnabled` | Enable password generation. | `bool` | `true` |
|
||||
| Name | Description | Type | Value |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------------- |
|
||||
| `authEnabled` | Enable password generation. | `bool` | `true` |
|
||||
| `tls` | TLS configuration for client-facing connections. When enabled, an nginx stream sidecar terminates TLS on port 6380. Intra-cluster traffic on 6379 (operator probes, sentinel, replication) remains plaintext. | `object` | `{}` |
|
||||
| `tls.enabled` | Enable TLS termination via nginx stream sidecar. | `bool` | `false` |
|
||||
| `tls.secretName` | Name of an existing kubernetes.io/tls secret. When non-empty, cert-manager Certificate is NOT rendered and the user manages the secret. | `string` | `""` |
|
||||
| `tls.issuerRef` | cert-manager Issuer reference. Used only when secretName is empty. | `object` | `{}` |
|
||||
| `tls.issuerRef.name` | Issuer/ClusterIssuer resource name. | `string` | `selfsigned-cluster-issuer` |
|
||||
| `tls.issuerRef.kind` | Either "Issuer" or "ClusterIssuer". | `string` | `ClusterIssuer` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
|
|
|||
35
packages/apps/redis/templates/_tls.tpl
Normal file
35
packages/apps/redis/templates/_tls.tpl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{{/*
|
||||
redis.tls.secretName — wraps cozy-lib.tls.secretName with redis-specific suffix.
|
||||
Returns either the user-provided tls.secretName or "<release>-redis-tls".
|
||||
*/}}
|
||||
{{- define "redis.tls.secretName" -}}
|
||||
{{- include "cozy-lib.tls.secretName" (dict
|
||||
"Release" .Release
|
||||
"suffix" "redis-tls"
|
||||
"secretName" .Values.tls.secretName
|
||||
) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
redis.tls.dnsNames — DNS SANs for the Redis TLS certificate.
|
||||
Covers the chart's TLS-only ClusterIP services (<release>-master for writes,
|
||||
<release>-replicas for reads) and the external LoadBalancer service across
|
||||
short, namespaced, and FQDN forms. The operator-managed rfs-* service is
|
||||
intentionally excluded because it serves plaintext on 6379 only.
|
||||
The external-lb variants are always included so toggling .Values.external
|
||||
later does not require certificate reissue.
|
||||
*/}}
|
||||
{{- define "redis.tls.dnsNames" -}}
|
||||
- {{ .Release.Name }}-master
|
||||
- {{ .Release.Name }}-master.{{ .Release.Namespace }}
|
||||
- {{ .Release.Name }}-master.{{ .Release.Namespace }}.svc
|
||||
- {{ .Release.Name }}-master.{{ .Release.Namespace }}.svc.cluster.local
|
||||
- {{ .Release.Name }}-replicas
|
||||
- {{ .Release.Name }}-replicas.{{ .Release.Namespace }}
|
||||
- {{ .Release.Name }}-replicas.{{ .Release.Namespace }}.svc
|
||||
- {{ .Release.Name }}-replicas.{{ .Release.Namespace }}.svc.cluster.local
|
||||
- {{ .Release.Name }}-external-lb
|
||||
- {{ .Release.Name }}-external-lb.{{ .Release.Namespace }}
|
||||
- {{ .Release.Name }}-external-lb.{{ .Release.Namespace }}.svc
|
||||
- {{ .Release.Name }}-external-lb.{{ .Release.Namespace }}.svc.cluster.local
|
||||
{{- end -}}
|
||||
|
|
@ -19,6 +19,9 @@ rules:
|
|||
- secrets
|
||||
resourceNames:
|
||||
- "{{ .Release.Name }}-auth"
|
||||
{{- if .Values.tls.enabled }}
|
||||
- "{{ include "redis.tls.secretName" . }}"
|
||||
{{- end }}
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- cozystack.io
|
||||
|
|
|
|||
|
|
@ -64,6 +64,48 @@ spec:
|
|||
- appendonly no
|
||||
- save ""
|
||||
{{- end }}
|
||||
{{- if .Values.tls.enabled }}
|
||||
extraContainers:
|
||||
- name: tls-proxy
|
||||
image: nginxinc/nginx-unprivileged:1.29-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 6380
|
||||
name: redis-tls
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: tls-certs
|
||||
mountPath: /etc/nginx/tls
|
||||
readOnly: true
|
||||
- name: nginx-tls-config
|
||||
mountPath: /etc/nginx/nginx.conf
|
||||
subPath: nginx.conf
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 16Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 6380
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 6380
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
extraVolumes:
|
||||
- name: tls-certs
|
||||
secret:
|
||||
secretName: {{ include "redis.tls.secretName" . }}
|
||||
- name: nginx-tls-config
|
||||
configMap:
|
||||
name: {{ .Release.Name }}-redis-tls-nginx
|
||||
{{- end }}
|
||||
{{- if .Values.authEnabled }}
|
||||
auth:
|
||||
secretPath: {{ .Release.Name }}-auth
|
||||
|
|
|
|||
|
|
@ -1,3 +1,47 @@
|
|||
{{- if .Values.tls.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-master
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/component: redis
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/name: {{ .Release.Name }}
|
||||
app.kubernetes.io/part-of: redis-failover
|
||||
redisfailovers-role: master
|
||||
ports:
|
||||
- name: redis-tls
|
||||
port: 6380
|
||||
targetPort: redis-tls
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-replicas
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/component: redis
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/name: {{ .Release.Name }}
|
||||
app.kubernetes.io/part-of: redis-failover
|
||||
redisfailovers-role: slave
|
||||
ports:
|
||||
- name: redis-tls
|
||||
port: 6380
|
||||
targetPort: redis-tls
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
{{- if .Values.external }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
|
@ -21,7 +65,15 @@ spec:
|
|||
app.kubernetes.io/part-of: redis-failover
|
||||
redisfailovers-role: master
|
||||
ports:
|
||||
{{- if .Values.tls.enabled }}
|
||||
- name: redis-tls
|
||||
port: 6380
|
||||
targetPort: redis-tls
|
||||
protocol: TCP
|
||||
{{- else }}
|
||||
- name: redis
|
||||
port: 6379
|
||||
targetPort: redis
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
22
packages/apps/redis/templates/tls-certificate.yaml
Normal file
22
packages/apps/redis/templates/tls-certificate.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{{- if and .Values.tls.enabled (not .Values.tls.secretName) }}
|
||||
{{- $dnsNames := list -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-master" .Release.Name) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-master.%s" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-master.%s.svc" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-master.%s.svc.cluster.local" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-replicas" .Release.Name) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-replicas.%s" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-replicas.%s.svc" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-replicas.%s.svc.cluster.local" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-external-lb" .Release.Name) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-external-lb.%s" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-external-lb.%s.svc" .Release.Name .Release.Namespace) -}}
|
||||
{{- $dnsNames = append $dnsNames (printf "%s-external-lb.%s.svc.cluster.local" .Release.Name .Release.Namespace) -}}
|
||||
{{- include "cozy-lib.tls.certificate" (dict
|
||||
"Release" .Release
|
||||
"name" (printf "%s-redis-tls" .Release.Name)
|
||||
"secretName" (include "redis.tls.secretName" .)
|
||||
"dnsNames" $dnsNames
|
||||
"issuerRef" .Values.tls.issuerRef
|
||||
) }}
|
||||
{{- end }}
|
||||
34
packages/apps/redis/templates/tls-nginx-config.yaml
Normal file
34
packages/apps/redis/templates/tls-nginx-config.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{{- if .Values.tls.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-redis-tls-nginx
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/component: redis-tls-proxy
|
||||
data:
|
||||
nginx.conf: |
|
||||
worker_processes 1;
|
||||
error_log /dev/stderr notice;
|
||||
pid /tmp/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
stream {
|
||||
upstream redis_backend {
|
||||
server 127.0.0.1:6379;
|
||||
}
|
||||
server {
|
||||
listen 6380 ssl;
|
||||
ssl_certificate /etc/nginx/tls/tls.crt;
|
||||
ssl_certificate_key /etc/nginx/tls/tls.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers off;
|
||||
proxy_pass redis_backend;
|
||||
proxy_timeout 1h;
|
||||
proxy_connect_timeout 5s;
|
||||
}
|
||||
}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
suite: Dashboard RBAC includes TLS secret
|
||||
templates:
|
||||
- templates/dashboard-resourcemap.yaml
|
||||
release:
|
||||
namespace: tenant-test
|
||||
tests:
|
||||
- it: TLS secret not in RBAC when tls.enabled is false
|
||||
set:
|
||||
tls:
|
||||
enabled: false
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: Role
|
||||
asserts:
|
||||
- notContains:
|
||||
path: rules[?(@.resources[0]=='secrets')].resourceNames
|
||||
content: RELEASE-NAME-redis-tls
|
||||
|
||||
- it: TLS secret added to RBAC when tls.enabled is true
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: Role
|
||||
asserts:
|
||||
- contains:
|
||||
path: rules[?(@.resources[0]=='secrets')].resourceNames
|
||||
content: RELEASE-NAME-redis-tls
|
||||
84
packages/apps/redis/tests/redisfailover-tls_test.yaml
Normal file
84
packages/apps/redis/tests/redisfailover-tls_test.yaml
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
suite: RedisFailover TLS sidecar injection
|
||||
templates:
|
||||
- templates/redisfailover.yaml
|
||||
tests:
|
||||
- it: does not inject extraContainers when tls.enabled is false
|
||||
set:
|
||||
tls:
|
||||
enabled: false
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: RedisFailover
|
||||
asserts:
|
||||
- notExists:
|
||||
path: spec.redis.extraContainers
|
||||
- notExists:
|
||||
path: spec.redis.extraVolumes
|
||||
|
||||
- it: injects nginx tls-proxy sidecar when tls.enabled is true
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: RedisFailover
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.redis.extraContainers[0].name
|
||||
value: tls-proxy
|
||||
- matchRegex:
|
||||
path: spec.redis.extraContainers[0].image
|
||||
pattern: "nginx"
|
||||
- equal:
|
||||
path: spec.redis.extraContainers[0].ports[0].containerPort
|
||||
value: 6380
|
||||
- equal:
|
||||
path: spec.redis.extraContainers[0].ports[0].name
|
||||
value: redis-tls
|
||||
|
||||
- it: extraVolumes reference auto-generated TLS secret when secretName empty
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: ""
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: RedisFailover
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.redis.extraVolumes
|
||||
content:
|
||||
name: tls-certs
|
||||
secret:
|
||||
secretName: RELEASE-NAME-redis-tls
|
||||
|
||||
- it: extraVolumes reference user-provided secret when secretName is set
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: my-custom-secret
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: RedisFailover
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.redis.extraVolumes
|
||||
content:
|
||||
name: tls-certs
|
||||
secret:
|
||||
secretName: my-custom-secret
|
||||
|
||||
- it: extraVolumes include nginx config ConfigMap
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: kind
|
||||
value: RedisFailover
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.redis.extraVolumes
|
||||
content:
|
||||
name: nginx-tls-config
|
||||
configMap:
|
||||
name: RELEASE-NAME-redis-tls-nginx
|
||||
179
packages/apps/redis/tests/service-tls_test.yaml
Normal file
179
packages/apps/redis/tests/service-tls_test.yaml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
suite: TLS service architecture
|
||||
templates:
|
||||
- templates/service.yaml
|
||||
tests:
|
||||
###################################################################
|
||||
# External LoadBalancer service (existing behavior, port switches) #
|
||||
###################################################################
|
||||
|
||||
- it: no service when external is false and TLS is false
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
- it: external true and TLS false renders LB on port 6379 plaintext
|
||||
set:
|
||||
external: true
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-external-lb
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.type
|
||||
value: LoadBalancer
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.ports[0].port
|
||||
value: 6379
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.ports[0].name
|
||||
value: redis
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.ports[0].targetPort
|
||||
value: redis
|
||||
|
||||
- it: external true and TLS true renders LB on port 6380 TLS
|
||||
set:
|
||||
external: true
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-external-lb
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.type
|
||||
value: LoadBalancer
|
||||
- equal:
|
||||
path: spec.ports[0].port
|
||||
value: 6380
|
||||
- equal:
|
||||
path: spec.ports[0].name
|
||||
value: redis-tls
|
||||
- equal:
|
||||
path: spec.ports[0].targetPort
|
||||
value: redis-tls
|
||||
|
||||
###################################################################
|
||||
# Internal ClusterIP services (only when TLS enabled) #
|
||||
###################################################################
|
||||
|
||||
- it: no master or replicas service when TLS disabled (external true)
|
||||
set:
|
||||
external: true
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-external-lb
|
||||
|
||||
- it: no services when TLS disabled and external false
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
- it: TLS enabled external false renders two ClusterIP services and no LB
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
|
||||
- it: TLS enabled external true renders three services (master + replicas + LB)
|
||||
set:
|
||||
external: true
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 3
|
||||
|
||||
###################
|
||||
# Master service #
|
||||
###################
|
||||
|
||||
- it: master service is a TLS ClusterIP on port 6380 targeting redis-tls
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-master
|
||||
asserts:
|
||||
- isKind:
|
||||
of: Service
|
||||
- equal:
|
||||
path: spec.type
|
||||
value: ClusterIP
|
||||
- equal:
|
||||
path: spec.ports[0].port
|
||||
value: 6380
|
||||
- equal:
|
||||
path: spec.ports[0].name
|
||||
value: redis-tls
|
||||
- equal:
|
||||
path: spec.ports[0].targetPort
|
||||
value: redis-tls
|
||||
- equal:
|
||||
path: spec.selector["redisfailovers-role"]
|
||||
value: master
|
||||
- equal:
|
||||
path: spec.selector["app.kubernetes.io/component"]
|
||||
value: redis
|
||||
|
||||
####################
|
||||
# Replicas service #
|
||||
####################
|
||||
|
||||
- it: replicas service is a TLS ClusterIP on port 6380 selecting slave role
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: true
|
||||
documentSelector:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-replicas
|
||||
asserts:
|
||||
- isKind:
|
||||
of: Service
|
||||
- equal:
|
||||
path: spec.type
|
||||
value: ClusterIP
|
||||
- equal:
|
||||
path: spec.ports[0].port
|
||||
value: 6380
|
||||
- equal:
|
||||
path: spec.ports[0].name
|
||||
value: redis-tls
|
||||
- equal:
|
||||
path: spec.ports[0].targetPort
|
||||
value: redis-tls
|
||||
- equal:
|
||||
path: spec.selector["redisfailovers-role"]
|
||||
value: slave
|
||||
- equal:
|
||||
path: spec.selector["app.kubernetes.io/component"]
|
||||
value: redis
|
||||
76
packages/apps/redis/tests/tls-certificate_test.yaml
Normal file
76
packages/apps/redis/tests/tls-certificate_test.yaml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
suite: TLS Certificate
|
||||
templates:
|
||||
- templates/tls-certificate.yaml
|
||||
tests:
|
||||
- it: does not render when tls.enabled is false
|
||||
set:
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments: { count: 0 }
|
||||
|
||||
- it: does not render when tls.secretName is provided (user owns secret)
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: my-existing-secret
|
||||
asserts:
|
||||
- hasDocuments: { count: 0 }
|
||||
|
||||
- it: renders Certificate when tls.enabled and secretName empty
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: ""
|
||||
asserts:
|
||||
- hasDocuments: { count: 1 }
|
||||
- isKind: { of: Certificate }
|
||||
- equal: { path: apiVersion, value: cert-manager.io/v1 }
|
||||
- equal: { path: spec.secretName, value: RELEASE-NAME-redis-tls }
|
||||
|
||||
- it: uses default issuerRef
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- equal: { path: spec.issuerRef.name, value: selfsigned-cluster-issuer }
|
||||
- equal: { path: spec.issuerRef.kind, value: ClusterIssuer }
|
||||
|
||||
- it: respects custom issuerRef
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
issuerRef:
|
||||
name: letsencrypt-prod
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- equal: { path: spec.issuerRef.name, value: letsencrypt-prod }
|
||||
|
||||
- it: dnsNames include master, replicas and external-lb service variants
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-master }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-master.NAMESPACE.svc.cluster.local }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-replicas }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-replicas.NAMESPACE.svc.cluster.local }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-external-lb }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-external-lb.NAMESPACE.svc.cluster.local }
|
||||
|
||||
- it: dnsNames do not contain rfs- operator plaintext service
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- notContains: { path: spec.dnsNames, content: rfs-RELEASE-NAME }
|
||||
- notContains: { path: spec.dnsNames, content: rfs-RELEASE-NAME.NAMESPACE.svc.cluster.local }
|
||||
|
||||
- it: dnsNames include external-lb variants even when external is false
|
||||
set:
|
||||
external: false
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-external-lb }
|
||||
- contains: { path: spec.dnsNames, content: RELEASE-NAME-external-lb.NAMESPACE.svc.cluster.local }
|
||||
44
packages/apps/redis/tests/tls-nginx-config_test.yaml
Normal file
44
packages/apps/redis/tests/tls-nginx-config_test.yaml
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
suite: TLS nginx ConfigMap
|
||||
templates:
|
||||
- templates/tls-nginx-config.yaml
|
||||
tests:
|
||||
- it: does not render when tls.enabled is false
|
||||
set:
|
||||
tls:
|
||||
enabled: false
|
||||
asserts:
|
||||
- hasDocuments: { count: 0 }
|
||||
|
||||
- it: renders ConfigMap when tls.enabled is true
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- hasDocuments: { count: 1 }
|
||||
- isKind: { of: ConfigMap }
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: RELEASE-NAME-redis-tls-nginx
|
||||
- exists:
|
||||
path: data["nginx.conf"]
|
||||
|
||||
- it: nginx config contains required directives
|
||||
set:
|
||||
tls:
|
||||
enabled: true
|
||||
asserts:
|
||||
- matchRegex:
|
||||
path: data["nginx.conf"]
|
||||
pattern: "listen 6380 ssl"
|
||||
- matchRegex:
|
||||
path: data["nginx.conf"]
|
||||
pattern: "127\\.0\\.0\\.1:6379"
|
||||
- matchRegex:
|
||||
path: data["nginx.conf"]
|
||||
pattern: "ssl_certificate /etc/nginx/tls/tls\\.crt"
|
||||
- matchRegex:
|
||||
path: data["nginx.conf"]
|
||||
pattern: "ssl_certificate_key /etc/nginx/tls/tls\\.key"
|
||||
- matchRegex:
|
||||
path: data["nginx.conf"]
|
||||
pattern: "ssl_protocols TLSv1\\.2 TLSv1\\.3"
|
||||
|
|
@ -91,6 +91,49 @@
|
|||
"description": "Enable password generation.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"tls": {
|
||||
"description": "TLS configuration for client-facing connections. When enabled, an nginx stream sidecar terminates TLS on port 6380. Intra-cluster traffic on 6379 (operator probes, sentinel, replication) remains plaintext.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"required": [
|
||||
"enabled",
|
||||
"issuerRef",
|
||||
"secretName"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable TLS termination via nginx stream sidecar.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"issuerRef": {
|
||||
"description": "cert-manager Issuer reference. Used only when secretName is empty.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"required": [
|
||||
"kind",
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"description": "Either \"Issuer\" or \"ClusterIssuer\".",
|
||||
"type": "string",
|
||||
"default": "ClusterIssuer"
|
||||
},
|
||||
"name": {
|
||||
"description": "Issuer/ClusterIssuer resource name.",
|
||||
"type": "string",
|
||||
"default": "selfsigned-cluster-issuer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"secretName": {
|
||||
"description": "Name of an existing kubernetes.io/tls secret. When non-empty, cert-manager Certificate is NOT rendered and the user manages the secret.",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,3 +46,25 @@ version: v8
|
|||
|
||||
## @param {bool} authEnabled - Enable password generation.
|
||||
authEnabled: true
|
||||
|
||||
## @typedef {struct} TLSIssuerRef - cert-manager Issuer reference.
|
||||
## @field {string} name - Issuer/ClusterIssuer resource name.
|
||||
## @field {string} kind - Either "Issuer" or "ClusterIssuer".
|
||||
|
||||
## @typedef {struct} TLS - TLS configuration for client-facing connections.
|
||||
## @field {bool} enabled - Enable TLS termination via nginx stream sidecar.
|
||||
## @field {string} secretName - Name of an existing kubernetes.io/tls secret. When non-empty, cert-manager Certificate is NOT rendered and the user manages the secret.
|
||||
## @field {TLSIssuerRef} issuerRef - cert-manager Issuer reference. Used only when secretName is empty.
|
||||
|
||||
## TLS limitations:
|
||||
## - Internal connections must use <release>-master:6380 (writes) or <release>-replicas:6380 (reads). Direct connection only — no Sentinel-aware service discovery over TLS.
|
||||
## - Sentinel HA clients (rfs-*:26379) operate on plaintext and resolve master IP as <ip>:6379 (the operator hardcodes the plaintext port). To use Sentinel-aware clients with TLS, the operator would need patching — out of scope.
|
||||
## - For applications that need automatic master failover with TLS: use <release>-master:6380 (the K8s Service's selector follows the master role label, so it auto-routes to the new master after failover).
|
||||
|
||||
## @param {TLS} tls - TLS configuration for client-facing connections. When enabled, an nginx stream sidecar terminates TLS on port 6380. Intra-cluster traffic on 6379 (operator probes, sentinel, replication) remains plaintext.
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: ""
|
||||
issuerRef:
|
||||
name: "selfsigned-cluster-issuer"
|
||||
kind: "ClusterIssuer"
|
||||
|
|
|
|||
110
packages/library/cozy-lib/templates/_tls.tpl
Normal file
110
packages/library/cozy-lib/templates/_tls.tpl
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
{{/*
|
||||
TLS certificate helpers for cert-manager integration.
|
||||
These helpers render cert-manager Certificate CRs for services that need TLS.
|
||||
*/}}
|
||||
|
||||
{{/*
|
||||
Render a cert-manager Certificate CR.
|
||||
|
||||
Usage:
|
||||
{{- include "cozy-lib.tls.certificate" (dict
|
||||
"Release" .Release
|
||||
"name" "my-redis-tls"
|
||||
"secretName" "my-redis-tls-secret"
|
||||
"dnsNames" (list "redis-svc" "redis-svc.ns.svc")
|
||||
"issuerRef" (dict "name" "selfsigned-cluster-issuer" "kind" "ClusterIssuer")
|
||||
) }}
|
||||
|
||||
Parameters:
|
||||
- Release (required) - Helm release object, used for labels
|
||||
- name (required) - Certificate CR metadata.name
|
||||
- secretName (required) - TLS secret name to create (can be same as name). Required when calling directly. When using through tls.yaml, defaults to name.
|
||||
- dnsNames (required) - list of DNS SANs
|
||||
- issuerRef (required) - dict with "name" and "kind" (e.g. ClusterIssuer, Issuer)
|
||||
- duration (optional) - certificate duration, defaults to 8760h (1 year); empty strings are treated as unset
|
||||
- renewBefore (optional) - renewal window, defaults to 720h (30 days)
|
||||
- usages (optional) - list of key usages, defaults to ["server auth"] (not cert-manager's default of "digital signature, key encipherment"; this is a deliberate choice for TLS server certificates)
|
||||
*/}}
|
||||
{{- define "cozy-lib.tls.certificate" -}}
|
||||
{{- if not .Release -}}
|
||||
{{- fail "ERROR: \"Release\" is required for cozy-lib.tls.certificate. Pass the Helm release object via the context dict." -}}
|
||||
{{- end -}}
|
||||
{{- if not .name -}}
|
||||
{{- fail "ERROR: \"name\" is required for cozy-lib.tls.certificate. It sets the Certificate CR metadata.name." -}}
|
||||
{{- end -}}
|
||||
{{- if not .secretName -}}
|
||||
{{- fail "ERROR: \"secretName\" is required for cozy-lib.tls.certificate. Specify the TLS secret name to create." -}}
|
||||
{{- end -}}
|
||||
{{- if not .dnsNames -}}
|
||||
{{- fail "ERROR: \"dnsNames\" is required for cozy-lib.tls.certificate. Provide at least one DNS name as a list." -}}
|
||||
{{- end -}}
|
||||
{{- if not .issuerRef -}}
|
||||
{{- fail "ERROR: \"issuerRef\" is required for cozy-lib.tls.certificate. Provide a dict with \"name\" and \"kind\"." -}}
|
||||
{{- end -}}
|
||||
{{- if not .issuerRef.name -}}
|
||||
{{- fail "ERROR: \"issuerRef.name\" is required for cozy-lib.tls.certificate. Specify the ClusterIssuer or Issuer name." -}}
|
||||
{{- end -}}
|
||||
{{- if not .issuerRef.kind -}}
|
||||
{{- fail "ERROR: \"issuerRef.kind\" is required for cozy-lib.tls.certificate. Specify \"ClusterIssuer\" or \"Issuer\"." -}}
|
||||
{{- end -}}
|
||||
{{- if not (or (eq .issuerRef.kind "Issuer") (eq .issuerRef.kind "ClusterIssuer")) -}}
|
||||
{{- fail "ERROR: issuerRef.kind must be \"Issuer\" or \"ClusterIssuer\"" -}}
|
||||
{{- end -}}
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
secretName: {{ .secretName }}
|
||||
duration: {{ ternary "8760h" .duration (empty .duration) }}
|
||||
renewBefore: {{ ternary "720h" .renewBefore (empty .renewBefore) }}
|
||||
privateKey:
|
||||
algorithm: RSA
|
||||
size: 2048
|
||||
usages:
|
||||
{{- if .usages }}
|
||||
{{- range .usages }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
- server auth
|
||||
{{- end }}
|
||||
dnsNames:
|
||||
{{- range .dnsNames }}
|
||||
- {{ . }}
|
||||
{{- end }}
|
||||
issuerRef:
|
||||
name: {{ .issuerRef.name }}
|
||||
kind: {{ .issuerRef.kind }}
|
||||
group: cert-manager.io
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Convenience helper for chart consumers.
|
||||
Return a TLS secret name — either the user-provided value or a generated default.
|
||||
Useful in conditionals to decide whether to mount a TLS secret or render a Certificate CR.
|
||||
|
||||
Usage:
|
||||
{{- $tlsSecret := include "cozy-lib.tls.secretName" (dict "Release" .Release "suffix" "tls" "secretName" .Values.tls.secretName) }}
|
||||
|
||||
Parameters:
|
||||
- Release (required) - Helm release object
|
||||
- suffix (required) - suffix for generated name (e.g. "tls", "server-tls")
|
||||
- secretName (optional) - user-provided secret name; if set, returned as-is
|
||||
*/}}
|
||||
{{- define "cozy-lib.tls.secretName" -}}
|
||||
{{- if not .Release -}}
|
||||
{{- fail "ERROR: \"Release\" is required for cozy-lib.tls.secretName. Pass the Helm release object via the context dict." -}}
|
||||
{{- end -}}
|
||||
{{- if not .suffix -}}
|
||||
{{- fail "ERROR: \"suffix\" is required for cozy-lib.tls.secretName. Provide a suffix for the generated secret name (e.g. \"tls\", \"server-tls\")." -}}
|
||||
{{- end -}}
|
||||
{{- if not (empty .secretName) -}}
|
||||
{{- .secretName -}}
|
||||
{{- else -}}
|
||||
{{- printf "%s-%s" .Release.Name .suffix -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
|
@ -8,7 +8,7 @@ spec:
|
|||
plural: redises
|
||||
singular: redis
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of Redis replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each Redis replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"version":{"description":"Redis major version to deploy","type":"string","default":"v8","enum":["v8","v7"]},"authEnabled":{"description":"Enable password generation.","type":"boolean","default":true}}}
|
||||
{"title":"Chart Values","type":"object","properties":{"replicas":{"description":"Number of Redis replicas.","type":"integer","default":2},"resources":{"description":"Explicit CPU and memory configuration for each Redis replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Memory (RAM) available to each replica.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"size":{"description":"Persistent Volume Claim size available for application data.","default":"1Gi","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"version":{"description":"Redis major version to deploy","type":"string","default":"v8","enum":["v8","v7"]},"authEnabled":{"description":"Enable password generation.","type":"boolean","default":true},"tls":{"description":"TLS configuration for client-facing connections. When enabled, an nginx stream sidecar terminates TLS on port 6380. Intra-cluster traffic on 6379 (operator probes, sentinel, replication) remains plaintext.","type":"object","default":{},"required":["enabled","issuerRef","secretName"],"properties":{"enabled":{"description":"Enable TLS termination via nginx stream sidecar.","type":"boolean","default":false},"issuerRef":{"description":"cert-manager Issuer reference. Used only when secretName is empty.","type":"object","default":{},"required":["kind","name"],"properties":{"kind":{"description":"Either \"Issuer\" or \"ClusterIssuer\".","type":"string","default":"ClusterIssuer"},"name":{"description":"Issuer/ClusterIssuer resource name.","type":"string","default":"selfsigned-cluster-issuer"}}},"secretName":{"description":"Name of an existing kubernetes.io/tls secret. When non-empty, cert-manager Certificate is NOT rendered and the user manages the secret.","type":"string","default":""}}}}}
|
||||
release:
|
||||
prefix: redis-
|
||||
labels:
|
||||
|
|
@ -25,12 +25,13 @@ spec:
|
|||
tags:
|
||||
- cache
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzIxMykiLz4KPHBhdGggZD0iTTEyMC4xNDkgOTUuNTQ5MUMxMTQuNTg2IDk4LjQ0ODUgODUuNzcwOSAxMTAuMjk2IDc5LjYzNjMgMTEzLjQ5NUM3My41MDE2IDExNi42OTMgNzAuMDkzNyAxMTYuNjYyIDY1LjI0NzIgMTE0LjM0NkM2MC40MDEyIDExMi4wMjkgMjkuNzM2NCA5OS42NDIzIDI0LjIxMjUgOTcuMDAxOUMyMS40NTE5IDk1LjY4MjcgMjAgOTQuNTY4NyAyMCA5My41MTY2VjgyLjk4MDlDMjAgODIuOTgwOSA1OS45MjIgNzQuMjkwMSA2Ni4zNjY5IDcxLjk3NzhDNzIuODExNSA2OS42NjU2IDc1LjA0NzYgNjkuNTgyMSA4MC41MzIgNzEuNTkxQzg2LjAxNzMgNzMuNjAwOCAxMTguODEyIDc5LjUxNzYgMTI0LjIzMyA4MS41MDI5TDEyNC4yMyA5MS44ODk2QzEyNC4yMzEgOTIuOTMxMSAxMjIuOTggOTQuMDczNiAxMjAuMTQ5IDk1LjU0OTFaIiBmaWxsPSIjOTEyNjI2Ii8+CjxwYXRoIGQ9Ik0xMjAuMTQ3IDg1LjA3NTJDMTE0LjU4NSA4Ny45NzM0IDg1Ljc3IDk5LjgyMTggNzkuNjM1NCAxMDMuMDJDNzMuNTAxMSAxMDYuMjE5IDcwLjA5MzIgMTA2LjE4NyA2NS4yNDcyIDEwMy44NzFDNjAuNDAwNyAxMDEuNTU1IDI5LjczNzEgODkuMTY2OCAyNC4yMTM2IDg2LjUyOEMxOC42OTAxIDgzLjg4NzYgMTguNTc0NCA4Mi4wNzA0IDI0LjAwMDMgNzkuOTQ1OEMyOS40MjYxIDc3LjgyMDUgNTkuOTIxNSA2NS44NTYxIDY2LjM2NzIgNjMuNTQzOEM3Mi44MTE4IDYxLjIzMjQgNzUuMDQ3NSA2MS4xNDgxIDgwLjUzMTkgNjMuMTU3OEM4Ni4wMTY4IDY1LjE2NjggMTE0LjY2IDc2LjU2NzYgMTIwLjA3OSA3OC41NTI1QzEyNS41MDEgODAuNTM5OSAxMjUuNzA5IDgyLjE3NjMgMTIwLjE0NyA4NS4wNzUyWiIgZmlsbD0iI0M2MzAyQiIvPgo8cGF0aCBkPSJNMTIwLjE0OSA3OC41MDJDMTE0LjU4NiA4MS40MDE4IDg1Ljc3MDkgOTMuMjQ5MyA3OS42MzYzIDk2LjQ0ODhDNzMuNTAxNiA5OS42NDYyIDcwLjA5MzcgOTkuNjE1MiA2NS4yNDcyIDk3LjI5ODVDNjAuNDAwOCA5NC45ODMgMjkuNzM2NCA4Mi41OTUyIDI0LjIxMjUgNzkuOTU0N0MyMS40NTE5IDc4LjYzNTUgMjAgNzcuNTIzMiAyMCA3Ni40NzA3VjY1LjkzMzhDMjAgNjUuOTMzOCA1OS45MjIgNTcuMjQzNCA2Ni4zNjY5IDU0LjkzMTFDNzIuODExNSA1Mi42MTg5IDc1LjA0NzYgNTIuNTM1IDgwLjUzMiA1NC41NDQzQzg2LjAxNzcgNTYuNTUzNiAxMTguODEzIDYyLjQ2OTMgMTI0LjIzMyA2NC40NTVMMTI0LjIzIDc0Ljg0MjhDMTI0LjIzMSA3NS44ODQgMTIyLjk4IDc3LjAyNjQgMTIwLjE0OSA3OC41MDJaIiBmaWxsPSIjOTEyNjI2Ii8+CjxwYXRoIGQ9Ik0xMjAuMTQ3IDY4LjAyODJDMTE0LjU4NSA3MC45MjcxIDg1Ljc3IDgyLjc3NDcgNzkuNjM1NCA4NS45NzM3QzczLjUwMTEgODkuMTcxNiA3MC4wOTMyIDg5LjE0MDIgNjUuMjQ3MiA4Ni44MjM1QzYwLjQwMDcgODQuNTA4NCAyOS43MzcxIDcyLjEyMDEgMjQuMjEzNiA2OS40ODA5QzE4LjY5MDEgNjYuODQxMyAxOC41NzQ0IDY1LjAyMzcgMjQuMDAwMyA2Mi44OTg0QzI5LjQyNjEgNjAuNzc0MiA1OS45MjE5IDQ4LjgwOSA2Ni4zNjcyIDQ2LjQ5NzJDNzIuODExOCA0NC4xODUzIDc1LjA0NzUgNDQuMTAxNCA4MC41MzE5IDQ2LjExMDhDODYuMDE2OCA0OC4xMTk3IDExNC42NiA1OS41MTk3IDEyMC4wNzkgNjEuNTA1NUMxMjUuNTAxIDYzLjQ5MjQgMTI1LjcwOSA2NS4xMjkyIDEyMC4xNDcgNjguMDI4MloiIGZpbGw9IiNDNjMwMkIiLz4KPHBhdGggZD0iTTEyMC4xNDkgNjAuODIyNEMxMTQuNTg2IDYzLjcyMTQgODUuNzcwOSA3NS41Njk4IDc5LjYzNjMgNzguNzY5MkM3My41MDE2IDgxLjk2NzEgNzAuMDkzNyA4MS45MzU3IDY1LjI0NzIgNzkuNjE5QzYwLjQwMDggNzcuMzAzNSAyOS43MzY0IDY0LjkxNTIgMjQuMjEyNSA2Mi4yNzZDMjEuNDUxOSA2MC45NTU2IDIwIDU5Ljg0MjggMjAgNTguNzkxNVY0OC4yNTQyQzIwIDQ4LjI1NDIgNTkuOTIyIDM5LjU2NDIgNjYuMzY2OSAzNy4yNTI0QzcyLjgxMTUgMzQuOTM5NyA3NS4wNDc2IDM0Ljg1NjcgODAuNTMyIDM2Ljg2NTZDODYuMDE3NyAzOC44NzQ5IDExOC44MTMgNDQuNzkwNSAxMjQuMjMzIDQ2Ljc3NjNMMTI0LjIzIDU3LjE2MzdDMTI0LjIzMSA1OC4yMDQgMTIyLjk4IDU5LjM0NjUgMTIwLjE0OSA2MC44MjI0WiIgZmlsbD0iIzkxMjYyNiIvPgo8cGF0aCBkPSJNMTIwLjE0NyA1MC4zNDlDMTE0LjU4NSA1My4yNDc5IDg1Ljc2OTggNjUuMDk2MyA3OS42MzUyIDY4LjI5NDFDNzMuNTAwOSA3MS40OTIgNzAuMDkzIDcxLjQ2MDYgNjUuMjQ2OSA2OS4xNDUxQzYwLjQwMDkgNjYuODI4MyAyOS43MzY5IDU0LjQ0MDkgMjQuMjEzOCA1MS44MDEzQzE4LjY4OTkgNDkuMTYyMSAxOC41NzQ2IDQ3LjM0NDEgMjQgNDUuMjE5MkMyOS40MjU5IDQzLjA5NDYgNTkuOTIxNyAzMS4xMzEgNjYuMzY3IDI4LjgxODRDNzIuODExNiAyNi41MDYxIDc1LjA0NzMgMjYuNDIzIDgwLjUzMTcgMjguNDMyNEM4Ni4wMTY2IDMwLjQ0MTcgMTE0LjY1OSA0MS44NDE4IDEyMC4wNzkgNDMuODI3NUMxMjUuNTAxIDQ1LjgxMjggMTI1LjcwOSA0Ny40NSAxMjAuMTQ3IDUwLjM0OVoiIGZpbGw9IiNDNjMwMkIiLz4KPHBhdGggZD0iTTg0Ljg1NDEgNDAuMDk5NEw3NS44OTI2IDQxLjAyOThMNzMuODg2NSA0NS44NTdMNzAuNjQ2MyA0MC40NzAzTDYwLjI5ODMgMzkuNTQwNEw2OC4wMTk3IDM2Ljc1NThMNjUuNzAzIDMyLjQ4MTRMNzIuOTMyMSAzNS4zMDg4TDc5Ljc0NzEgMzMuMDc3NUw3Ny45MDUyIDM3LjQ5NzJMODQuODU0MSA0MC4wOTk0Wk03My4zNTE1IDYzLjUxODRMNTYuNjI2NiA1Ni41ODE2TDgwLjU5MiA1Mi45MDI5TDczLjM1MTUgNjMuNTE4NFpNNTAuMTYzNyA0Mi43ODI2QzU3LjIzODEgNDIuNzgyNiA2Mi45NzMgNDUuMDA1NyA2Mi45NzMgNDcuNzQ3NUM2Mi45NzMgNTAuNDkwMSA1Ny4yMzgxIDUyLjcxMjggNTAuMTYzNyA1Mi43MTI4QzQzLjA4OTMgNTIuNzEyOCAzNy4zNTQ1IDUwLjQ4OTcgMzcuMzU0NSA0Ny43NDc1QzM3LjM1NDUgNDUuMDA1NyA0My4wODkzIDQyLjc4MjYgNTAuMTYzNyA0Mi43ODI2WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTk1LjQ0MzQgNDEuNDE3NEwxMDkuNjI3IDQ3LjAyMjRMOTUuNDU1NiA1Mi42MjJMOTUuNDQzNCA0MS40MTc0WiIgZmlsbD0iIzYyMUIxQyIvPgo8cGF0aCBkPSJNNzkuNzUyOSA0Ny42MjYxTDk1LjQ0NDkgNDEuNDE4OUw5NS40NTcxIDUyLjYyMzZMOTMuOTE4NCA1My4yMjU0TDc5Ljc1MjkgNDcuNjI2MVoiIGZpbGw9IiM5QTI5MjgiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODNfMzIxMyIgeDE9IjE4OSIgeTE9IjIxMC41IiB4Mj0iMCIgeTI9IjAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0E4MDAwMCIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkNGQ0YiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "authEnabled"]]
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "authEnabled"], ["spec", "tls"], ["spec", "tls", "enabled"], ["spec", "tls", "secretName"], ["spec", "tls", "issuerRef"], ["spec", "tls", "issuerRef", "name"], ["spec", "tls", "issuerRef", "kind"]]
|
||||
secrets:
|
||||
exclude: []
|
||||
include:
|
||||
- resourceNames:
|
||||
- redis-{{ .name }}-auth
|
||||
- redis-{{ .name }}-redis-tls
|
||||
services:
|
||||
exclude: []
|
||||
include:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{{/*
|
||||
Test-only template: calls cozy-lib.tls.certificate without Release
|
||||
to verify the validation guard triggers.
|
||||
*/}}
|
||||
{{- $certArgs := dict "name" .Values.tls.name "secretName" .Values.tls.secretName "dnsNames" .Values.tls.dnsNames "issuerRef" .Values.tls.issuerRef -}}
|
||||
{{- include "cozy-lib.tls.certificate" $certArgs -}}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{{/*
|
||||
Test-only template: calls cozy-lib.tls.certificate without secretName
|
||||
to verify the validation guard triggers.
|
||||
*/}}
|
||||
{{- $certArgs := dict "Release" .Release "name" .Values.tls.name "dnsNames" .Values.tls.dnsNames "issuerRef" .Values.tls.issuerRef -}}
|
||||
{{- include "cozy-lib.tls.certificate" $certArgs -}}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{{/*
|
||||
Test-only template: calls cozy-lib.tls.secretName without Release
|
||||
to verify the validation guard triggers.
|
||||
*/}}
|
||||
{{- $secretName := include "cozy-lib.tls.secretName" (dict "suffix" "tls" "secretName" .Values.tlsSecretName) -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-secretname-missing-release
|
||||
data:
|
||||
secretName: {{ $secretName | quote }}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{{/*
|
||||
Test-only template: calls cozy-lib.tls.secretName without suffix
|
||||
to verify the validation guard triggers.
|
||||
*/}}
|
||||
{{- $secretName := include "cozy-lib.tls.secretName" (dict "Release" .Release "secretName" .Values.tlsSecretName) -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-secretname-missing-suffix
|
||||
data:
|
||||
secretName: {{ $secretName | quote }}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{{- $secretName := include "cozy-lib.tls.secretName" (dict "Release" .Release "suffix" .Values.tlsSuffix "secretName" .Values.tlsSecretName) -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-secretname
|
||||
data:
|
||||
secretName: {{ $secretName | quote }}
|
||||
6
packages/tests/cozy-lib-tests/templates/tests/tls.yaml
Normal file
6
packages/tests/cozy-lib-tests/templates/tests/tls.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{{- if .Values.tls }}
|
||||
{{- $certArgs := dict "Release" .Release "name" .Values.tls.name "secretName" (default .Values.tls.name .Values.tls.secretName) "dnsNames" .Values.tls.dnsNames "issuerRef" .Values.tls.issuerRef "usages" (.Values.tls.usages | default list) -}}
|
||||
{{- if and .Values.tls.duration (ne .Values.tls.duration "") }}{{- $_ := set $certArgs "duration" .Values.tls.duration }}{{- end -}}
|
||||
{{- if and .Values.tls.renewBefore (ne .Values.tls.renewBefore "") }}{{- $_ := set $certArgs "renewBefore" .Values.tls.renewBefore }}{{- end -}}
|
||||
{{- include "cozy-lib.tls.certificate" $certArgs -}}
|
||||
{{- end }}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
suite: TLS certificate helper - Release validation
|
||||
|
||||
templates:
|
||||
- templates/tests/tls-missing-release.yaml
|
||||
|
||||
tests:
|
||||
# Missing Release triggers a validation error
|
||||
- it: errors when Release is not provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: missing-release-tls
|
||||
secretName: missing-release-tls-secret
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "Release" is required for cozy-lib.tls.certificate. Pass the Helm release object via the context dict.'
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
suite: TLS certificate helper - secretName validation
|
||||
|
||||
templates:
|
||||
- templates/tests/tls-missing-secretname.yaml
|
||||
|
||||
tests:
|
||||
# Missing secretName triggers a validation error
|
||||
- it: errors when secretName is not provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: missing-secret-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "secretName" is required for cozy-lib.tls.certificate. Specify the TLS secret name to create.'
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
suite: TLS secretName helper - Release validation
|
||||
|
||||
templates:
|
||||
- templates/tests/tls-secretname-missing-release.yaml
|
||||
|
||||
tests:
|
||||
- it: errors when Release is not provided
|
||||
release:
|
||||
name: my-app
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tlsSecretName: my-custom-tls-secret
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "Release" is required for cozy-lib.tls.secretName. Pass the Helm release object via the context dict.'
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
suite: TLS secretName helper - suffix validation
|
||||
|
||||
templates:
|
||||
- templates/tests/tls-secretname-missing-suffix.yaml
|
||||
|
||||
tests:
|
||||
- it: errors when suffix is not provided
|
||||
release:
|
||||
name: my-app
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tlsSecretName: ""
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "suffix" is required for cozy-lib.tls.secretName. Provide a suffix for the generated secret name (e.g. "tls", "server-tls").'
|
||||
46
packages/tests/cozy-lib-tests/tests/tls_secretname_test.yaml
Normal file
46
packages/tests/cozy-lib-tests/tests/tls_secretname_test.yaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
suite: TLS secretName helper
|
||||
|
||||
templates:
|
||||
- templates/tests/tls-secretname.yaml
|
||||
|
||||
tests:
|
||||
- it: returns user-provided secretName when set
|
||||
release:
|
||||
name: my-app
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tlsSecretName: my-custom-tls-secret
|
||||
tlsSuffix: tls
|
||||
asserts:
|
||||
- equal:
|
||||
path: data.secretName
|
||||
value: my-custom-tls-secret
|
||||
|
||||
- it: generates default secretName from release name and suffix when secretName is omitted
|
||||
release:
|
||||
name: my-app
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tlsSuffix: tls
|
||||
asserts:
|
||||
- equal:
|
||||
path: data.secretName
|
||||
value: my-app-tls
|
||||
|
||||
- it: generates default secretName when secretName is an empty string
|
||||
release:
|
||||
name: my-app
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tlsSecretName: ""
|
||||
tlsSuffix: tls
|
||||
asserts:
|
||||
- equal:
|
||||
path: data.secretName
|
||||
value: my-app-tls
|
||||
350
packages/tests/cozy-lib-tests/tests/tls_test.yaml
Normal file
350
packages/tests/cozy-lib-tests/tests/tls_test.yaml
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
suite: TLS certificate helper
|
||||
|
||||
templates:
|
||||
- templates/tests/tls.yaml
|
||||
|
||||
tests:
|
||||
# Certificate CR renders with all required fields
|
||||
- it: renders a Certificate CR with all required fields
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- isKind:
|
||||
of: Certificate
|
||||
- isAPIVersion:
|
||||
of: cert-manager.io/v1
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: my-redis-tls
|
||||
- equal:
|
||||
path: spec.secretName
|
||||
value: my-redis-tls-secret
|
||||
- equal:
|
||||
path: spec.privateKey.algorithm
|
||||
value: RSA
|
||||
- equal:
|
||||
path: spec.privateKey.size
|
||||
value: 2048
|
||||
|
||||
# dnsNames are correctly set
|
||||
- it: sets dnsNames correctly
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.dnsNames
|
||||
value:
|
||||
- redis-svc
|
||||
- redis-svc.tenant-test.svc
|
||||
|
||||
# issuerRef is correctly set
|
||||
- it: sets issuerRef correctly
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.issuerRef.name
|
||||
value: selfsigned-cluster-issuer
|
||||
- equal:
|
||||
path: spec.issuerRef.kind
|
||||
value: ClusterIssuer
|
||||
- equal:
|
||||
path: spec.issuerRef.group
|
||||
value: cert-manager.io
|
||||
|
||||
# Labels include release info
|
||||
- it: includes standard labels
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.labels["app.kubernetes.io/instance"]
|
||||
value: test-release
|
||||
- equal:
|
||||
path: metadata.labels["app.kubernetes.io/managed-by"]
|
||||
value: Helm
|
||||
|
||||
# Default duration and renewBefore are used when not provided
|
||||
- it: uses default duration and renewBefore
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.duration
|
||||
value: 8760h
|
||||
- equal:
|
||||
path: spec.renewBefore
|
||||
value: 720h
|
||||
|
||||
# Custom duration and renewBefore work when provided
|
||||
- it: uses custom duration and renewBefore when provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: custom-tls
|
||||
secretName: custom-tls-secret
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: my-issuer
|
||||
kind: Issuer
|
||||
duration: 43800h
|
||||
renewBefore: 360h
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.duration
|
||||
value: 43800h
|
||||
- equal:
|
||||
path: spec.renewBefore
|
||||
value: 360h
|
||||
|
||||
# Default usages is server auth
|
||||
- it: uses server auth as default usage
|
||||
values:
|
||||
- tls_values.yaml
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.usages
|
||||
value:
|
||||
- server auth
|
||||
|
||||
# Custom usages work when provided
|
||||
- it: uses custom usages when provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: client-tls
|
||||
secretName: client-tls-secret
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: my-issuer
|
||||
kind: ClusterIssuer
|
||||
usages:
|
||||
- server auth
|
||||
- client auth
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.usages
|
||||
value:
|
||||
- server auth
|
||||
- client auth
|
||||
|
||||
# secretName defaults to name when not provided
|
||||
- it: uses name as secretName when secretName not provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: my-tls-cert
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: my-tls-cert
|
||||
- equal:
|
||||
path: spec.secretName
|
||||
value: my-tls-cert
|
||||
|
||||
# Issuer kind is passed through to Certificate CR
|
||||
- it: passes issuerRef kind to Certificate CR
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: test-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.issuerRef.kind
|
||||
value: ClusterIssuer
|
||||
|
||||
# Namespace-scoped Issuer also works
|
||||
- it: supports namespace-scoped Issuer
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: test-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: my-namespace-issuer
|
||||
kind: Issuer
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.issuerRef.kind
|
||||
value: Issuer
|
||||
- equal:
|
||||
path: spec.issuerRef.name
|
||||
value: my-namespace-issuer
|
||||
|
||||
# TLS disabled produces zero documents
|
||||
- it: produces zero documents when tls is not set
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
# TLS disabled when tls is explicitly null
|
||||
- it: produces zero documents when tls is null
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls: null
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
# Empty dnsNames triggers a validation error
|
||||
- it: errors when dnsNames is empty
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: empty-dns-tls
|
||||
dnsNames: []
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "dnsNames" is required for cozy-lib.tls.certificate. Provide at least one DNS name as a list.'
|
||||
|
||||
# Missing issuerRef name triggers a validation error
|
||||
- it: errors when issuerRef name is not provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: missing-issuer-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
kind: ClusterIssuer
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "issuerRef.name" is required for cozy-lib.tls.certificate. Specify the ClusterIssuer or Issuer name.'
|
||||
|
||||
# Missing issuerRef kind triggers a validation error
|
||||
- it: errors when issuerRef kind is not provided
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: missing-kind-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: my-issuer
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: "issuerRef.kind" is required for cozy-lib.tls.certificate. Specify "ClusterIssuer" or "Issuer".'
|
||||
|
||||
# Invalid issuerRef kind triggers a validation error
|
||||
- it: errors when issuerRef kind is not Issuer or ClusterIssuer
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: invalid-kind-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: my-issuer
|
||||
kind: InvalidKind
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: 'ERROR: issuerRef.kind must be "Issuer" or "ClusterIssuer"'
|
||||
|
||||
# Empty string duration should fall back to default 8760h
|
||||
- it: uses default duration when duration is an empty string
|
||||
release:
|
||||
name: test-release
|
||||
namespace: tenant-test
|
||||
set:
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
tls:
|
||||
name: empty-duration-tls
|
||||
dnsNames:
|
||||
- svc.example
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
duration: ""
|
||||
renewBefore: ""
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- equal:
|
||||
path: spec.duration
|
||||
value: 8760h
|
||||
- equal:
|
||||
path: spec.renewBefore
|
||||
value: 720h
|
||||
12
packages/tests/cozy-lib-tests/tests/tls_values.yaml
Normal file
12
packages/tests/cozy-lib-tests/tests/tls_values.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
tls:
|
||||
name: my-redis-tls
|
||||
secretName: my-redis-tls-secret
|
||||
dnsNames:
|
||||
- redis-svc
|
||||
- redis-svc.tenant-test.svc
|
||||
issuerRef:
|
||||
name: selfsigned-cluster-issuer
|
||||
kind: ClusterIssuer
|
||||
|
||||
_cluster: {}
|
||||
_namespace: {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue