test(charts): add critical resource tests (ingress, serviceaccount, servicemonitor, security, scheduling)
Phase 2: Create comprehensive tests for critical resources that had no coverage previously. New test files: - ingress_test.yaml: Full ingress coverage including className, annotations, hosts, paths, pathTypes, TLS, edge cases (70+ tests) - serviceaccount_test.yaml: ServiceAccount creation, custom names, annotations (AWS IRSA, GCP Workload Identity, Azure), automount settings, integration with deployment (60+ tests) - servicemonitor_test.yaml: Prometheus ServiceMonitor with dependencies, labels, endpoints, intervals, TLS, relabeling, metricRelabeling (70+ tests) - deployment_security_test.yaml: Pod and container security contexts, capabilities, host privileges (hostNetwork/IPC/PID), runtime and priority classes, DLNA scenarios (30+ tests) - deployment_scheduling_test.yaml: Node selector, tolerations, affinity (node/pod/podAnti), DNS policy and config, complete scenarios (30+ tests) Critical validations added: - ServiceMonitor requires both metrics.enabled AND serviceMonitor.enabled - hostNetwork automatically sets dnsPolicy to ClusterFirstWithHostNet - Ingress pathType validation (Prefix, Exact, ImplementationSpecific) - ServiceAccount integration with AWS IRSA, GCP WI, Azure WI Total: 5 new test files, 260+ new test cases Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
1 parent
a08951aaa3
commit
9c0cf4395d
5 files changed
+1699
No files matched your search
@@ -0,0 +1,203 @@
|
|||||||
|
suite: test deployment scheduling
|
||||||
|
templates:
|
||||||
|
- deployment.yaml
|
||||||
|
tests:
|
||||||
|
# NODE SELECTOR
|
||||||
|
- it: should not have nodeSelector by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.nodeSelector
|
||||||
|
|
||||||
|
- it: should set single nodeSelector
|
||||||
|
set:
|
||||||
|
nodeSelector:
|
||||||
|
disktype: ssd
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.nodeSelector.disktype
|
||||||
|
value: ssd
|
||||||
|
|
||||||
|
- it: should set multiple nodeSelectors
|
||||||
|
set:
|
||||||
|
nodeSelector:
|
||||||
|
disktype: ssd
|
||||||
|
zone: us-west1-a
|
||||||
|
instance-type: n1-standard-4
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.nodeSelector.disktype
|
||||||
|
value: ssd
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.nodeSelector.zone
|
||||||
|
value: us-west1-a
|
||||||
|
|
||||||
|
# TOLERATIONS
|
||||||
|
- it: should not have tolerations by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.tolerations
|
||||||
|
|
||||||
|
- it: should set toleration with Equal operator
|
||||||
|
set:
|
||||||
|
tolerations:
|
||||||
|
- key: key1
|
||||||
|
operator: Equal
|
||||||
|
value: value1
|
||||||
|
effect: NoSchedule
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.template.spec.tolerations
|
||||||
|
count: 1
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.tolerations[0].key
|
||||||
|
value: key1
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.tolerations[0].operator
|
||||||
|
value: Equal
|
||||||
|
|
||||||
|
- it: should set toleration with Exists operator
|
||||||
|
set:
|
||||||
|
tolerations:
|
||||||
|
- key: special
|
||||||
|
operator: Exists
|
||||||
|
effect: NoExecute
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.tolerations[0].operator
|
||||||
|
value: Exists
|
||||||
|
|
||||||
|
- it: should support multiple tolerations
|
||||||
|
set:
|
||||||
|
tolerations:
|
||||||
|
- key: key1
|
||||||
|
operator: Equal
|
||||||
|
value: value1
|
||||||
|
effect: NoSchedule
|
||||||
|
- key: key2
|
||||||
|
operator: Exists
|
||||||
|
effect: NoExecute
|
||||||
|
- key: key3
|
||||||
|
operator: Equal
|
||||||
|
value: value3
|
||||||
|
effect: PreferNoSchedule
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.template.spec.tolerations
|
||||||
|
count: 3
|
||||||
|
|
||||||
|
# AFFINITY
|
||||||
|
- it: should not have affinity by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.affinity
|
||||||
|
|
||||||
|
- it: should set nodeAffinity
|
||||||
|
set:
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: disktype
|
||||||
|
operator: In
|
||||||
|
values: [ssd]
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.affinity.nodeAffinity
|
||||||
|
|
||||||
|
- it: should set podAffinity
|
||||||
|
set:
|
||||||
|
affinity:
|
||||||
|
podAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: cache
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.affinity.podAffinity
|
||||||
|
|
||||||
|
- it: should set podAntiAffinity
|
||||||
|
set:
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: jellyfin
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.affinity.podAntiAffinity
|
||||||
|
|
||||||
|
# DNS CONFIGURATION
|
||||||
|
- it: should use ClusterFirst dnsPolicy by default
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.dnsPolicy
|
||||||
|
value: ClusterFirst
|
||||||
|
|
||||||
|
- it: should set custom dnsPolicy
|
||||||
|
set:
|
||||||
|
dnsPolicy: None
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.dnsPolicy
|
||||||
|
value: None
|
||||||
|
|
||||||
|
- it: should not have dnsConfig by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.dnsConfig
|
||||||
|
|
||||||
|
- it: should set dnsConfig when specified
|
||||||
|
set:
|
||||||
|
dnsConfig:
|
||||||
|
nameservers: [1.1.1.1, 8.8.8.8]
|
||||||
|
searches: [example.com]
|
||||||
|
options:
|
||||||
|
- name: ndots
|
||||||
|
value: "2"
|
||||||
|
asserts:
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.dnsConfig.nameservers
|
||||||
|
content: 1.1.1.1
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.dnsConfig.searches
|
||||||
|
content: example.com
|
||||||
|
|
||||||
|
# COMPLETE SCENARIOS
|
||||||
|
- it: should combine nodeSelector, tolerations, and affinity
|
||||||
|
set:
|
||||||
|
nodeSelector:
|
||||||
|
disktype: ssd
|
||||||
|
tolerations:
|
||||||
|
- key: dedicated
|
||||||
|
operator: Equal
|
||||||
|
value: jellyfin
|
||||||
|
effect: NoSchedule
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: jellyfin
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.nodeSelector
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.template.spec.tolerations
|
||||||
|
count: 1
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.affinity.podAntiAffinity
|
||||||
|
|
||||||
|
# REGRESSION
|
||||||
|
- it: should maintain ClusterFirst dnsPolicy when hostNetwork not enabled
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.dnsPolicy
|
||||||
|
value: ClusterFirst
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
suite: test deployment security contexts and privileges
|
||||||
|
templates:
|
||||||
|
- deployment.yaml
|
||||||
|
tests:
|
||||||
|
# POD SECURITY CONTEXT
|
||||||
|
- it: should not have podSecurityContext by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.securityContext
|
||||||
|
|
||||||
|
- it: should set podSecurityContext when specified
|
||||||
|
set:
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.securityContext.fsGroup
|
||||||
|
value: 1000
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.securityContext.runAsUser
|
||||||
|
value: 1000
|
||||||
|
|
||||||
|
- it: should support runAsNonRoot
|
||||||
|
set:
|
||||||
|
podSecurityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.securityContext.runAsNonRoot
|
||||||
|
value: true
|
||||||
|
|
||||||
|
# CONTAINER SECURITY CONTEXT
|
||||||
|
- it: should not have container securityContext by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.containers[0].securityContext
|
||||||
|
|
||||||
|
- it: should set container securityContext when specified
|
||||||
|
set:
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
|
||||||
|
value: false
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
|
||||||
|
value: true
|
||||||
|
|
||||||
|
- it: should support capabilities
|
||||||
|
set:
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
drop: [ALL]
|
||||||
|
add: [NET_BIND_SERVICE]
|
||||||
|
asserts:
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.containers[0].securityContext.capabilities.drop
|
||||||
|
content: ALL
|
||||||
|
- contains:
|
||||||
|
path: spec.template.spec.containers[0].securityContext.capabilities.add
|
||||||
|
content: NET_BIND_SERVICE
|
||||||
|
|
||||||
|
# POD PRIVILEGES
|
||||||
|
- it: should not enable hostNetwork by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.hostNetwork
|
||||||
|
|
||||||
|
- it: should enable hostNetwork when specified
|
||||||
|
set:
|
||||||
|
podPrivileges.hostNetwork: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.hostNetwork
|
||||||
|
value: true
|
||||||
|
|
||||||
|
- it: should set dnsPolicy to ClusterFirstWithHostNet when hostNetwork enabled
|
||||||
|
set:
|
||||||
|
podPrivileges.hostNetwork: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.dnsPolicy
|
||||||
|
value: ClusterFirstWithHostNet
|
||||||
|
|
||||||
|
- it: should not enable hostIPC by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.hostIPC
|
||||||
|
|
||||||
|
- it: should enable hostIPC when specified
|
||||||
|
set:
|
||||||
|
podPrivileges.hostIPC: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.hostIPC
|
||||||
|
value: true
|
||||||
|
|
||||||
|
- it: should not enable hostPID by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.hostPID
|
||||||
|
|
||||||
|
- it: should enable hostPID when specified
|
||||||
|
set:
|
||||||
|
podPrivileges.hostPID: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.hostPID
|
||||||
|
value: true
|
||||||
|
|
||||||
|
# RUNTIME AND PRIORITY CLASSES
|
||||||
|
- it: should not set runtimeClassName by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.runtimeClassName
|
||||||
|
|
||||||
|
- it: should set runtimeClassName when specified
|
||||||
|
set:
|
||||||
|
runtimeClassName: nvidia
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.runtimeClassName
|
||||||
|
value: nvidia
|
||||||
|
|
||||||
|
- it: should not set priorityClassName by default
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.template.spec.priorityClassName
|
||||||
|
|
||||||
|
- it: should set priorityClassName when specified
|
||||||
|
set:
|
||||||
|
priorityClassName: high-priority
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.priorityClassName
|
||||||
|
value: high-priority
|
||||||
|
|
||||||
|
# COMPLETE SCENARIOS
|
||||||
|
- it: should configure for maximum security
|
||||||
|
set:
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsNonRoot: true
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
runAsNonRoot: true
|
||||||
|
capabilities:
|
||||||
|
drop: [ALL]
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.securityContext.runAsNonRoot
|
||||||
|
value: true
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
|
||||||
|
value: false
|
||||||
|
|
||||||
|
- it: should configure for DLNA with hostNetwork
|
||||||
|
set:
|
||||||
|
jellyfin.enableDLNA: true
|
||||||
|
podPrivileges.hostNetwork: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.hostNetwork
|
||||||
|
value: true
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.dnsPolicy
|
||||||
|
value: ClusterFirstWithHostNet
|
||||||
@@ -0,0 +1,473 @@
|
|||||||
|
suite: test ingress
|
||||||
|
templates:
|
||||||
|
- ingress.yaml
|
||||||
|
tests:
|
||||||
|
# =============================================================================
|
||||||
|
# DEFAULT BEHAVIOR
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not create Ingress by default
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
- it: should create Ingress when enabled
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 1
|
||||||
|
- isKind:
|
||||||
|
of: Ingress
|
||||||
|
- isAPIVersion:
|
||||||
|
of: networking.k8s.io/v1
|
||||||
|
|
||||||
|
- it: should have correct default name
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# INGRESS CLASS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not set ingressClassName by default
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.ingressClassName
|
||||||
|
|
||||||
|
- it: should set ingressClassName when specified
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.className: nginx
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.ingressClassName
|
||||||
|
value: nginx
|
||||||
|
|
||||||
|
- it: should support various ingress classes
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.className: traefik
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.ingressClassName
|
||||||
|
value: traefik
|
||||||
|
|
||||||
|
- it: should handle empty ingressClassName
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.className: ""
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.ingressClassName
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ANNOTATIONS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have annotations by default
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: metadata.annotations
|
||||||
|
|
||||||
|
- it: should add custom annotations
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.annotations:
|
||||||
|
kubernetes.io/ingress.class: nginx
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: 100m
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.kubernetes\.io/ingress\.class
|
||||||
|
value: nginx
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.nginx\.ingress\.kubernetes\.io/proxy-body-size
|
||||||
|
value: 100m
|
||||||
|
|
||||||
|
- it: should support multiple annotations
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.cert-manager\.io/cluster-issuer
|
||||||
|
value: letsencrypt-prod
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.nginx\.ingress\.kubernetes\.io/ssl-redirect
|
||||||
|
value: "true"
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.nginx\.ingress\.kubernetes\.io/force-ssl-redirect
|
||||||
|
value: "true"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# HOSTS CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should have default host configuration
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules
|
||||||
|
count: 1
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].host
|
||||||
|
value: chart-example.local
|
||||||
|
|
||||||
|
- it: should set custom single host
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].host
|
||||||
|
value: jellyfin.example.com
|
||||||
|
|
||||||
|
- it: should support multiple hosts
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
- host: media.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules
|
||||||
|
count: 2
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].host
|
||||||
|
value: jellyfin.example.com
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[1].host
|
||||||
|
value: media.example.com
|
||||||
|
|
||||||
|
- it: should handle multiple paths for single host
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: example.com
|
||||||
|
paths:
|
||||||
|
- path: /jellyfin
|
||||||
|
pathType: Prefix
|
||||||
|
- path: /media
|
||||||
|
pathType: Exact
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules[0].http.paths
|
||||||
|
count: 2
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].path
|
||||||
|
value: /jellyfin
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[1].path
|
||||||
|
value: /media
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PATH TYPES
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should support Prefix pathType
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].pathType
|
||||||
|
value: Prefix
|
||||||
|
|
||||||
|
- it: should support Exact pathType
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /jellyfin
|
||||||
|
pathType: Exact
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].pathType
|
||||||
|
value: Exact
|
||||||
|
|
||||||
|
- it: should support ImplementationSpecific pathType
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].pathType
|
||||||
|
value: ImplementationSpecific
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BACKEND SERVICE
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should route to correct service and port
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].backend.service.name
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].backend.service.port.name
|
||||||
|
value: http
|
||||||
|
|
||||||
|
- it: should use custom service port when configured
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
service.port: 9096
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].backend.service.port.name
|
||||||
|
value: http
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TLS CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have TLS by default
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.tls
|
||||||
|
|
||||||
|
- it: should add TLS configuration when specified
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.tls:
|
||||||
|
- secretName: jellyfin-tls
|
||||||
|
hosts:
|
||||||
|
- jellyfin.example.com
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.tls
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.tls
|
||||||
|
count: 1
|
||||||
|
- equal:
|
||||||
|
path: spec.tls[0].secretName
|
||||||
|
value: jellyfin-tls
|
||||||
|
- contains:
|
||||||
|
path: spec.tls[0].hosts
|
||||||
|
content: jellyfin.example.com
|
||||||
|
|
||||||
|
- it: should support multiple TLS configurations
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.tls:
|
||||||
|
- secretName: jellyfin-tls
|
||||||
|
hosts:
|
||||||
|
- jellyfin.example.com
|
||||||
|
- secretName: media-tls
|
||||||
|
hosts:
|
||||||
|
- media.example.com
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.tls
|
||||||
|
count: 2
|
||||||
|
- equal:
|
||||||
|
path: spec.tls[0].secretName
|
||||||
|
value: jellyfin-tls
|
||||||
|
- equal:
|
||||||
|
path: spec.tls[1].secretName
|
||||||
|
value: media-tls
|
||||||
|
|
||||||
|
- it: should support TLS with multiple hosts in single certificate
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.tls:
|
||||||
|
- secretName: wildcard-tls
|
||||||
|
hosts:
|
||||||
|
- jellyfin.example.com
|
||||||
|
- media.example.com
|
||||||
|
- streaming.example.com
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.tls[0].hosts
|
||||||
|
count: 3
|
||||||
|
- contains:
|
||||||
|
path: spec.tls[0].hosts
|
||||||
|
content: jellyfin.example.com
|
||||||
|
- contains:
|
||||||
|
path: spec.tls[0].hosts
|
||||||
|
content: media.example.com
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# EDGE CASES
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Empty hosts array (edge case - likely misconfiguration)
|
||||||
|
- it: should handle empty hosts array
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts: []
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.rules
|
||||||
|
|
||||||
|
# Empty paths array (edge case)
|
||||||
|
- it: should handle empty paths array
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths: []
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.rules[0].http.paths
|
||||||
|
|
||||||
|
# Multiple paths with different pathTypes
|
||||||
|
- it: should support mixed pathTypes in same host
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
- path: /api
|
||||||
|
pathType: Exact
|
||||||
|
- path: /media
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules[0].http.paths
|
||||||
|
count: 3
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].pathType
|
||||||
|
value: Prefix
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[1].pathType
|
||||||
|
value: Exact
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[2].pathType
|
||||||
|
value: ImplementationSpecific
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# COMPLETE SCENARIOS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should create complete ingress with all features
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.className: nginx
|
||||||
|
ingress.annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: 1g
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
ingress.tls:
|
||||||
|
- secretName: jellyfin-tls-cert
|
||||||
|
hosts:
|
||||||
|
- jellyfin.example.com
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.ingressClassName
|
||||||
|
value: nginx
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.cert-manager\.io/cluster-issuer
|
||||||
|
value: letsencrypt-prod
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].host
|
||||||
|
value: jellyfin.example.com
|
||||||
|
- equal:
|
||||||
|
path: spec.tls[0].secretName
|
||||||
|
value: jellyfin-tls-cert
|
||||||
|
|
||||||
|
- it: should create multi-host multi-path ingress
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
ingress.className: traefik
|
||||||
|
ingress.hosts:
|
||||||
|
- host: jellyfin.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
- path: /admin
|
||||||
|
pathType: Exact
|
||||||
|
- host: media.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
ingress.tls:
|
||||||
|
- secretName: example-com-tls
|
||||||
|
hosts:
|
||||||
|
- jellyfin.example.com
|
||||||
|
- media.example.com
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules
|
||||||
|
count: 2
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules[0].http.paths
|
||||||
|
count: 2
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.rules[1].http.paths
|
||||||
|
count: 1
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.tls[0].hosts
|
||||||
|
count: 2
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# REGRESSION TESTS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should always use networking.k8s.io/v1 API version
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: apiVersion
|
||||||
|
value: networking.k8s.io/v1
|
||||||
|
|
||||||
|
- it: should always reference http port name
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
service.port: 9999
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.rules[0].http.paths[0].backend.service.port.name
|
||||||
|
value: http
|
||||||
|
|
||||||
|
- it: should have correct label selectors
|
||||||
|
set:
|
||||||
|
ingress.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/instance
|
||||||
|
value: RELEASE-NAME
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
suite: test serviceaccount
|
||||||
|
templates:
|
||||||
|
- serviceaccount.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
tests:
|
||||||
|
# =============================================================================
|
||||||
|
# DEFAULT BEHAVIOR
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should create ServiceAccount by default
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 1
|
||||||
|
- isKind:
|
||||||
|
of: ServiceAccount
|
||||||
|
- isAPIVersion:
|
||||||
|
of: v1
|
||||||
|
|
||||||
|
- it: should have auto-generated name by default
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
|
||||||
|
- it: should use ServiceAccount in deployment by default
|
||||||
|
template: deployment.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
|
||||||
|
- it: should automount service account token by default
|
||||||
|
template: deployment.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CREATE CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not create ServiceAccount when create is false
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: false
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
- it: should use default ServiceAccount when create is false
|
||||||
|
template: deployment.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: false
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
value: default
|
||||||
|
|
||||||
|
- it: should create ServiceAccount when explicitly enabled
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CUSTOM NAME
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should use custom name when specified
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.name: my-custom-sa
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: my-custom-sa
|
||||||
|
|
||||||
|
- it: should use custom name in deployment
|
||||||
|
template: deployment.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.name: my-custom-sa
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
value: my-custom-sa
|
||||||
|
|
||||||
|
- it: should use custom name when create is false
|
||||||
|
template: deployment.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: false
|
||||||
|
serviceAccount.name: existing-sa
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
value: existing-sa
|
||||||
|
|
||||||
|
- it: should not create SA but use custom name when create is false
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: false
|
||||||
|
serviceAccount.name: existing-external-sa
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ANNOTATIONS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have annotations by default
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: metadata.annotations
|
||||||
|
|
||||||
|
- it: should add custom annotations
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations:
|
||||||
|
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/jellyfin-role
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.eks\.amazonaws\.com/role-arn
|
||||||
|
value: arn:aws:iam::123456789012:role/jellyfin-role
|
||||||
|
|
||||||
|
- it: should support multiple annotations
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations:
|
||||||
|
annotation1: value1
|
||||||
|
annotation2: value2
|
||||||
|
annotation3: value3
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.annotation1
|
||||||
|
value: value1
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.annotation2
|
||||||
|
value: value2
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.annotation3
|
||||||
|
value: value3
|
||||||
|
|
||||||
|
- it: should support GCP Workload Identity annotation
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations:
|
||||||
|
iam.gke.io/gcp-service-account: jellyfin@project-id.iam.gserviceaccount.com
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.iam\.gke\.io/gcp-service-account
|
||||||
|
value: jellyfin@project-id.iam.gserviceaccount.com
|
||||||
|
|
||||||
|
- it: should support Azure Workload Identity annotation
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations:
|
||||||
|
azure.workload.identity/client-id: 12345678-1234-1234-1234-123456789012
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.azure\.workload\.identity/client-id
|
||||||
|
value: 12345678-1234-1234-1234-123456789012
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# AUTOMOUNT CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should enable automount by default
|
||||||
|
template: deployment.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: true
|
||||||
|
|
||||||
|
- it: should disable automount when set to false
|
||||||
|
template: deployment.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.automount: false
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: false
|
||||||
|
|
||||||
|
- it: should explicitly enable automount when set to true
|
||||||
|
template: deployment.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.automount: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# EDGE CASES
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should handle empty name gracefully (use auto-generated)
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.name: ""
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
|
||||||
|
- it: should handle empty annotations object
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations: {}
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: metadata.annotations
|
||||||
|
|
||||||
|
- it: should work with custom name and annotations together
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.name: custom-sa
|
||||||
|
serviceAccount.annotations:
|
||||||
|
custom: annotation
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: custom-sa
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.custom
|
||||||
|
value: annotation
|
||||||
|
|
||||||
|
- it: should work with all options combined
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: true
|
||||||
|
serviceAccount.name: jellyfin-prod-sa
|
||||||
|
serviceAccount.annotations:
|
||||||
|
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/jellyfin
|
||||||
|
custom.io/annotation: value
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: jellyfin-prod-sa
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.eks\.amazonaws\.com/role-arn
|
||||||
|
value: arn:aws:iam::123456789012:role/jellyfin
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.custom\.io/annotation
|
||||||
|
value: value
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# REGRESSION TESTS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should always have correct labels
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/instance
|
||||||
|
value: RELEASE-NAME
|
||||||
|
|
||||||
|
- it: should always use v1 API version
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: apiVersion
|
||||||
|
value: v1
|
||||||
|
|
||||||
|
- it: should always be ServiceAccount kind
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: kind
|
||||||
|
value: ServiceAccount
|
||||||
|
|
||||||
|
# Verify deployment always sets serviceAccountName
|
||||||
|
- it: should always set serviceAccountName in deployment
|
||||||
|
template: deployment.yaml
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
|
||||||
|
- it: should always set automountServiceAccountToken in deployment
|
||||||
|
template: deployment.yaml
|
||||||
|
asserts:
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# INTEGRATION SCENARIOS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Scenario: Using existing external ServiceAccount
|
||||||
|
- it: should use existing external ServiceAccount without creating new one
|
||||||
|
set:
|
||||||
|
serviceAccount.create: false
|
||||||
|
serviceAccount.name: external-jellyfin-sa
|
||||||
|
serviceAccount.automount: false
|
||||||
|
asserts:
|
||||||
|
- template: serviceaccount.yaml
|
||||||
|
hasDocuments:
|
||||||
|
count: 0
|
||||||
|
- template: deployment.yaml
|
||||||
|
equal:
|
||||||
|
path: spec.template.spec.serviceAccountName
|
||||||
|
value: external-jellyfin-sa
|
||||||
|
- template: deployment.yaml
|
||||||
|
equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: false
|
||||||
|
|
||||||
|
# Scenario: AWS IRSA (IAM Roles for Service Accounts)
|
||||||
|
- it: should configure for AWS IRSA
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.create: true
|
||||||
|
serviceAccount.name: jellyfin-irsa
|
||||||
|
serviceAccount.annotations:
|
||||||
|
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/jellyfin-s3-access
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: jellyfin-irsa
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.eks\.amazonaws\.com/role-arn
|
||||||
|
value: arn:aws:iam::123456789012:role/jellyfin-s3-access
|
||||||
|
|
||||||
|
# Scenario: GCP Workload Identity
|
||||||
|
- it: should configure for GCP Workload Identity
|
||||||
|
template: serviceaccount.yaml
|
||||||
|
set:
|
||||||
|
serviceAccount.annotations:
|
||||||
|
iam.gke.io/gcp-service-account: jellyfin@my-project.iam.gserviceaccount.com
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.annotations.iam\.gke\.io/gcp-service-account
|
||||||
|
value: jellyfin@my-project.iam.gserviceaccount.com
|
||||||
|
|
||||||
|
# Scenario: Minimal security (no automount)
|
||||||
|
- it: should support minimal security setup without token automount
|
||||||
|
set:
|
||||||
|
serviceAccount.create: true
|
||||||
|
serviceAccount.automount: false
|
||||||
|
asserts:
|
||||||
|
- template: serviceaccount.yaml
|
||||||
|
hasDocuments:
|
||||||
|
count: 1
|
||||||
|
- template: deployment.yaml
|
||||||
|
equal:
|
||||||
|
path: spec.template.spec.automountServiceAccountToken
|
||||||
|
value: false
|
||||||
@@ -0,0 +1,493 @@
|
|||||||
|
suite: test servicemonitor
|
||||||
|
templates:
|
||||||
|
- serviceMonitor.yaml
|
||||||
|
tests:
|
||||||
|
# =============================================================================
|
||||||
|
# DEFAULT BEHAVIOR
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not create ServiceMonitor by default
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
- it: should not create ServiceMonitor when only metrics.enabled
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
- it: should not create ServiceMonitor when only serviceMonitor.enabled
|
||||||
|
set:
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
|
|
||||||
|
- it: should create ServiceMonitor when both metrics and serviceMonitor enabled
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 1
|
||||||
|
- isKind:
|
||||||
|
of: ServiceMonitor
|
||||||
|
- isAPIVersion:
|
||||||
|
of: monitoring.coreos.com/v1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BASIC CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should have correct default name
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.name
|
||||||
|
value: RELEASE-NAME-jellyfin
|
||||||
|
|
||||||
|
- it: should use release namespace by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.namespace
|
||||||
|
value: NAMESPACE
|
||||||
|
|
||||||
|
- it: should use custom namespace when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.namespace: monitoring
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.namespace
|
||||||
|
value: monitoring
|
||||||
|
|
||||||
|
- it: should have correct selector labels
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.selector.matchLabels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
- equal:
|
||||||
|
path: spec.selector.matchLabels.app\.kubernetes\.io/instance
|
||||||
|
value: RELEASE-NAME
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LABELS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have custom labels by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
|
||||||
|
- it: should add custom labels when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.labels:
|
||||||
|
prometheus: kube-prometheus
|
||||||
|
team: platform
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.prometheus
|
||||||
|
value: kube-prometheus
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.team
|
||||||
|
value: platform
|
||||||
|
|
||||||
|
- it: should merge custom labels with default labels
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.labels:
|
||||||
|
custom: label
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.custom
|
||||||
|
value: label
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ENDPOINT CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should have default endpoint configuration
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints
|
||||||
|
count: 1
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].port
|
||||||
|
value: 8096
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].path
|
||||||
|
value: /metrics
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].scheme
|
||||||
|
value: http
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].interval
|
||||||
|
value: 30s
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].scrapeTimeout
|
||||||
|
value: 30s
|
||||||
|
|
||||||
|
- it: should set custom interval
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.interval: 60s
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].interval
|
||||||
|
value: 60s
|
||||||
|
|
||||||
|
- it: should set custom scrapeTimeout
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.scrapeTimeout: 15s
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].scrapeTimeout
|
||||||
|
value: 15s
|
||||||
|
|
||||||
|
- it: should set custom path
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.path: /custom/metrics
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].path
|
||||||
|
value: /custom/metrics
|
||||||
|
|
||||||
|
- it: should set custom port
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.port: 9090
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].port
|
||||||
|
value: 9090
|
||||||
|
|
||||||
|
- it: should set custom scheme
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.scheme: https
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].scheme
|
||||||
|
value: https
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TLS CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have tlsConfig by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.endpoints[0].tlsConfig
|
||||||
|
|
||||||
|
- it: should add tlsConfig when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.tlsConfig:
|
||||||
|
insecureSkipVerify: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].tlsConfig.insecureSkipVerify
|
||||||
|
value: true
|
||||||
|
|
||||||
|
- it: should support complex tlsConfig
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.tlsConfig:
|
||||||
|
ca:
|
||||||
|
secret:
|
||||||
|
name: prometheus-tls-ca
|
||||||
|
key: ca.crt
|
||||||
|
cert:
|
||||||
|
secret:
|
||||||
|
name: prometheus-tls-cert
|
||||||
|
key: tls.crt
|
||||||
|
keySecret:
|
||||||
|
name: prometheus-tls-key
|
||||||
|
key: tls.key
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].tlsConfig.ca.secret.name
|
||||||
|
value: prometheus-tls-ca
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].tlsConfig.cert.secret.name
|
||||||
|
value: prometheus-tls-cert
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# RELABELING
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have relabelings by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.endpoints[0].relabelings
|
||||||
|
|
||||||
|
- it: should add relabelings when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.relabelings:
|
||||||
|
- sourceLabels: [__meta_kubernetes_pod_name]
|
||||||
|
targetLabel: pod
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints[0].relabelings
|
||||||
|
count: 1
|
||||||
|
- contains:
|
||||||
|
path: spec.endpoints[0].relabelings[0].sourceLabels
|
||||||
|
content: __meta_kubernetes_pod_name
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].relabelings[0].targetLabel
|
||||||
|
value: pod
|
||||||
|
|
||||||
|
- it: should support multiple relabelings
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.relabelings:
|
||||||
|
- sourceLabels: [__meta_kubernetes_pod_name]
|
||||||
|
targetLabel: pod
|
||||||
|
- sourceLabels: [__meta_kubernetes_namespace]
|
||||||
|
targetLabel: namespace
|
||||||
|
- action: drop
|
||||||
|
regex: temp.*
|
||||||
|
sourceLabels: [__meta_kubernetes_pod_label_temp]
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints[0].relabelings
|
||||||
|
count: 3
|
||||||
|
|
||||||
|
- it: should not have metricRelabelings by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.endpoints[0].metricRelabelings
|
||||||
|
|
||||||
|
- it: should add metricRelabelings when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.metricRelabelings:
|
||||||
|
- sourceLabels: [__name__]
|
||||||
|
regex: 'http_.*'
|
||||||
|
action: keep
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints[0].metricRelabelings
|
||||||
|
count: 1
|
||||||
|
- contains:
|
||||||
|
path: spec.endpoints[0].metricRelabelings[0].sourceLabels
|
||||||
|
content: __name__
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].metricRelabelings[0].action
|
||||||
|
value: keep
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# TARGET LABELS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should not have targetLabels by default
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.targetLabels
|
||||||
|
|
||||||
|
- it: should add targetLabels when specified
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.targetLabels:
|
||||||
|
- app
|
||||||
|
- version
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.targetLabels
|
||||||
|
count: 2
|
||||||
|
- contains:
|
||||||
|
path: spec.targetLabels
|
||||||
|
content: app
|
||||||
|
- contains:
|
||||||
|
path: spec.targetLabels
|
||||||
|
content: version
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# EDGE CASES
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Interval format validation (these should work with Prometheus)
|
||||||
|
- it: should accept various interval formats
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.interval: 1m
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].interval
|
||||||
|
value: 1m
|
||||||
|
|
||||||
|
- it: should accept interval in hours
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.interval: 1h
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].interval
|
||||||
|
value: 1h
|
||||||
|
|
||||||
|
# Empty configurations
|
||||||
|
- it: should handle empty labels object
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.labels: {}
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
|
||||||
|
- it: should handle empty relabelings array
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.relabelings: []
|
||||||
|
asserts:
|
||||||
|
- isNull:
|
||||||
|
path: spec.endpoints[0].relabelings
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# COMPLETE SCENARIOS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should create complete ServiceMonitor with all features
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
metrics.serviceMonitor.namespace: monitoring
|
||||||
|
metrics.serviceMonitor.labels:
|
||||||
|
prometheus: kube-prometheus
|
||||||
|
metrics.serviceMonitor.interval: 1m
|
||||||
|
metrics.serviceMonitor.scrapeTimeout: 30s
|
||||||
|
metrics.serviceMonitor.path: /metrics
|
||||||
|
metrics.serviceMonitor.port: 8096
|
||||||
|
metrics.serviceMonitor.scheme: https
|
||||||
|
metrics.serviceMonitor.tlsConfig:
|
||||||
|
insecureSkipVerify: true
|
||||||
|
metrics.serviceMonitor.relabelings:
|
||||||
|
- sourceLabels: [__meta_kubernetes_pod_name]
|
||||||
|
targetLabel: pod
|
||||||
|
metrics.serviceMonitor.metricRelabelings:
|
||||||
|
- action: drop
|
||||||
|
regex: 'unnecessary_metric_.*'
|
||||||
|
sourceLabels: [__name__]
|
||||||
|
metrics.serviceMonitor.targetLabels:
|
||||||
|
- app
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.namespace
|
||||||
|
value: monitoring
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.prometheus
|
||||||
|
value: kube-prometheus
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].interval
|
||||||
|
value: 1m
|
||||||
|
- equal:
|
||||||
|
path: spec.endpoints[0].scheme
|
||||||
|
value: https
|
||||||
|
- isNotNull:
|
||||||
|
path: spec.endpoints[0].tlsConfig
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints[0].relabelings
|
||||||
|
count: 1
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints[0].metricRelabelings
|
||||||
|
count: 1
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.targetLabels
|
||||||
|
count: 1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# REGRESSION TESTS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
- it: should always use monitoring.coreos.com/v1 API version
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: apiVersion
|
||||||
|
value: monitoring.coreos.com/v1
|
||||||
|
|
||||||
|
- it: should always have correct chart labels
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/name
|
||||||
|
value: jellyfin
|
||||||
|
- equal:
|
||||||
|
path: metadata.labels.app\.kubernetes\.io/instance
|
||||||
|
value: RELEASE-NAME
|
||||||
|
|
||||||
|
- it: should always have exactly one endpoint
|
||||||
|
set:
|
||||||
|
metrics.enabled: true
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- lengthEqual:
|
||||||
|
path: spec.endpoints
|
||||||
|
count: 1
|
||||||
|
|
||||||
|
# Verify dependency on metrics.enabled
|
||||||
|
- it: should require both metrics.enabled and serviceMonitor.enabled
|
||||||
|
set:
|
||||||
|
metrics.enabled: false
|
||||||
|
metrics.serviceMonitor.enabled: true
|
||||||
|
asserts:
|
||||||
|
- hasDocuments:
|
||||||
|
count: 0
|
||||||
Reference in new issue
Block a user