Ivan Ka 3f42ce9716
chore(tests): Add YAML-driven integration test framework for sources (#6158)
* chore(tests): added cross source tests

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): added cross source tests

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): added cross source tests

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): added cross source tests

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): Add YAML-driven integration test framework for sources

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): Add YAML-driven integration test framework for sources

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): Add YAML-driven integration test framework for sources

Co-authored-by: vflaux <38909103+vflaux@users.noreply.github.com>

* chore(tests): Add YAML-driven integration test framework for sources

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

* chore(tests): Add YAML-driven integration test framework for sources

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>

---------

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
Co-authored-by: vflaux <38909103+vflaux@users.noreply.github.com>
2026-03-16 15:19:38 +05:30

234 lines
8.2 KiB
YAML

# Integration Test Scenarios
#
# Schema Summary:
# | Field | Type | Description |
# |----------------------------------------|----------|------------------------------------------|
# | name | string | Test scenario name |
# | config.sources | []string | Sources to create: ingress, service |
# | config.defaultTargets | []string | --default-targets flag values |
# | config.forceDefaultTargets | bool | --force-default-targets flag |
# | config.targetNetFilter | []string | --target-net-filter flag values |
# | config.serviceTypeFilter | []string | --service-type-filter flag values |
# | resources | []object | K8s resources with optional dependencies |
# | resources[].resource | object | K8s resource (Ingress, Service, etc.) |
# | resources[].dependencies | object | Auto-generated dependent resources |
# | resources[].dependencies.pods.replicas | int | Number of pods to generate |
# | expected | []object | Expected endpoints |
# TODO:
# 1. Support to Endpoint.ResourceLabelKey
# 2. Support for Endpoint.RefObject
# 3. Support for Endpoint.ProviderSpecific
scenarios:
- name: headless-service-with-pods
description: >
Test that a headless Service with associated Pods
creates the correct DNS A records for each Pod IP.
config:
sources: ["service"]
targetNetFilter: ["10.0.0.1/32", "10.0.0.2/32"]
serviceTypeFilter: ["ClusterIP"]
resources:
- resource:
apiVersion: v1
kind: Service
metadata:
name: headless-svc
namespace: default
labels:
app: myapp
annotations:
external-dns.alpha.kubernetes.io/hostname: headless.example.com
spec:
type: ClusterIP
clusterIP: None
selector:
app: myapp
dependencies:
pods:
replicas: 3
expected:
- dnsName: headless.example.com
targets: ["10.0.0.1", "10.0.0.2"]
recordType: A
- dnsName: headless-svc-0.headless.example.com
targets: ["10.0.0.1"]
recordType: A
- dnsName: headless-svc-1.headless.example.com
targets: ["10.0.0.2"]
recordType: A
- name: service-loadbalancer-with-ip
description: >
Test that a Service of type LoadBalancer with an assigned IP
creates the correct DNS A record.
config:
sources: ["service"]
resources:
- resource:
apiVersion: v1
kind: Service
metadata:
name: test-service
namespace: default
annotations:
external-dns.alpha.kubernetes.io/hostname: svc.example.com
spec:
selector:
app.kubernetes.io/name: MyApp
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 1.2.3.4
expected:
- dnsName: svc.example.com
targets: ["1.2.3.4"]
recordType: A
- name: known-limitation-cross-source-dedup-not-applied
description: >
Documents a known limitation: when multiple ingresses share the same hostname,
ingressSource.Endpoints() merges their targets via MergeEndpoints() before the
dedupSource ever sees them. The resulting combined ingress endpoint
(["1.2.3.4","203.0.113.10"]) has different Targets.String() than the service
endpoint (["1.2.3.4"]), so the dedupSource keeps both — producing two A records
for the same hostname. Ideally the service endpoint would be absorbed into the
ingress one, but that would require cross-source target merging which does not
exist today. See the "service-and-ingress-same-hostname-and-ip-dedup" scenario
for a case where deduplication does work correctly.
config:
sources: ["service", "ingress"]
resources:
- resource:
apiVersion: v1
kind: Service
metadata:
name: test-service
namespace: default
annotations:
external-dns.alpha.kubernetes.io/hostname: example.local
spec:
selector:
app.kubernetes.io/name: MyApp
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 1.2.3.4
- resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: example.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
status:
loadBalancer:
ingress:
- ip: 203.0.113.10
- resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-with-same-host-and-status-as-service
namespace: kube-system
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: example.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
status:
loadBalancer:
ingress:
- ip: 1.2.3.4
expected:
# The ingress source merges all ingresses with the same hostname via MergeEndpoints(),
# so both ingresses (203.0.113.10 and 1.2.3.4) produce one combined endpoint.
- dnsName: example.local
targets: ["1.2.3.4", "203.0.113.10"]
recordType: A
# The service source produces a separate endpoint for the same hostname.
- dnsName: example.local
targets: ["1.2.3.4"]
recordType: A
- name: service-and-ingress-same-hostname-and-ip-dedup
description: >
Test that dedupSource correctly removes an exact duplicate endpoint.
Both the Service and the Ingress resolve example.local to the same IP
(1.2.3.4), so each source emits an identical endpoint
(RecordType=A, DNSName=example.local, Targets=["1.2.3.4"]).
The dedupSource key is RecordType+DNSName+SetIdentifier+Targets.String(),
which matches, so the second endpoint is dropped and only one A record survives.
config:
sources: ["service", "ingress"]
resources:
- resource:
apiVersion: v1
kind: Service
metadata:
name: test-service
namespace: default
annotations:
external-dns.alpha.kubernetes.io/hostname: example.local
spec:
selector:
app.kubernetes.io/name: MyApp
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 1.2.3.4
- resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: example.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
status:
loadBalancer:
ingress:
- ip: 1.2.3.4
expected:
- dnsName: example.local
targets: ["1.2.3.4"]
recordType: A