mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
Implement target annotation for more sources
This commit is contained in:
parent
859892fc72
commit
4a32aaec01
@ -4,30 +4,31 @@ ExternalDNS sources support a number of annotations on the Kubernetes resources
|
|||||||
|
|
||||||
The following table documents which sources support which annotations:
|
The following table documents which sources support which annotations:
|
||||||
|
|
||||||
| Source | controller | hostname | internal-hostname | target | ttl | (provider-specific) |
|
| Source | controller | hostname | internal-hostname | target | ttl | (provider-specific) |
|
||||||
|--------------|------------|----------|-------------------|---------|-----|---------------------|
|
|--------------|------------|----------|-------------------|---------|---------|---------------------|
|
||||||
| Ambassador | | | | | Yes | |
|
| Ambassador | | | | Yes | Yes | |
|
||||||
| Connector | | | | | | |
|
| Connector | | | | | | |
|
||||||
| Contour | Yes | Yes[^1] | | Yes | Yes | Yes |
|
| Contour | Yes | Yes[^1] | | Yes | Yes | Yes |
|
||||||
| CloudFoundry | | | | | | |
|
| CloudFoundry | | | | | | |
|
||||||
| CRD | | | | | | |
|
| CRD | | | | | | |
|
||||||
| F5 | | | | | Yes | |
|
| F5 | | | | Yes | Yes | |
|
||||||
| Gateway | Yes | Yes[^1] | | Yes[^4] | Yes | Yes |
|
| Gateway | Yes | Yes[^1] | | Yes[^4] | Yes | Yes |
|
||||||
| Gloo | | | | | Yes | Yes |
|
| Gloo | | | | Yes | Yes[^5] | Yes[^5] |
|
||||||
| Ingress | Yes | Yes[^1] | | Yes | Yes | Yes |
|
| Ingress | Yes | Yes[^1] | | Yes | Yes | Yes |
|
||||||
| Istio | Yes | Yes[^1] | | Yes | Yes | Yes |
|
| Istio | Yes | Yes[^1] | | Yes | Yes | Yes |
|
||||||
| Kong | | Yes | | | Yes | Yes |
|
| Kong | | Yes | | Yes | Yes | Yes |
|
||||||
| Node | Yes | | | | Yes | |
|
| Node | Yes | | | Yes | Yes | |
|
||||||
| OpenShift | Yes | Yes[^1] | | Yes | Yes | Yes |
|
| OpenShift | Yes | Yes[^1] | | Yes | Yes | Yes |
|
||||||
| Pod | | Yes | Yes | | | |
|
| Pod | | Yes | Yes | Yes | | |
|
||||||
| Service | Yes | Yes[^1] | Yes[^1][^2] | Yes[^3] | Yes | Yes |
|
| Service | Yes | Yes[^1] | Yes[^1][^2] | Yes[^3] | Yes | Yes |
|
||||||
| Skipper | Yes | Yes[^1] | | Yes | Yes | Yes |
|
| Skipper | Yes | Yes[^1] | | Yes | Yes | Yes |
|
||||||
| Traefik | | Yes | | Yes | Yes | Yes |
|
| Traefik | | Yes | | Yes | Yes | Yes |
|
||||||
|
|
||||||
[^1]: Unless the `--ignore-hostname-annotation` flag is specified.
|
[^1]: Unless the `--ignore-hostname-annotation` flag is specified.
|
||||||
[^2]: Only behaves differently than `hostname` for `Service`s of type `ClusterIP` or `LoadBalancer`.
|
[^2]: Only behaves differently than `hostname` for `Service`s of type `ClusterIP` or `LoadBalancer`.
|
||||||
[^3]: Also supported on `Pods` referenced from a headless `Service`'s `Endpoints`.
|
[^3]: Also supported on `Pods` referenced from a headless `Service`'s `Endpoints`.
|
||||||
[^4]: The annotation should be on the `Gateway`
|
[^4]: The annotation must be on the `Gateway`.
|
||||||
|
[^5]: The annotation must be on the listener's `VirtualService`.
|
||||||
|
|
||||||
## external-dns.alpha.kubernetes.io/access
|
## external-dns.alpha.kubernetes.io/access
|
||||||
|
|
||||||
|
@ -133,10 +133,13 @@ func (sc *ambassadorHostSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
targets, err := sc.targetsFromAmbassadorLoadBalancer(ctx, service)
|
targets := getTargetsFromTargetAnnotation(host.Annotations)
|
||||||
if err != nil {
|
if len(targets) == 0 {
|
||||||
log.Warningf("Could not find targets for service %s for Host %s: %v", service, fullname, err)
|
targets, err = sc.targetsFromAmbassadorLoadBalancer(ctx, service)
|
||||||
continue
|
if err != nil {
|
||||||
|
log.Warningf("Could not find targets for service %s for Host %s: %v", service, fullname, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostEndpoints, err := sc.endpointsFromHost(ctx, host, targets)
|
hostEndpoints, err := sc.endpointsFromHost(ctx, host, targets)
|
||||||
|
@ -152,37 +152,16 @@ func (vs *f5VirtualServerSource) endpointsFromVirtualServers(virtualServers []*f
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if virtualServer.Spec.VirtualServerAddress != "" {
|
targets := getTargetsFromTargetAnnotation(virtualServer.Annotations)
|
||||||
ep := &endpoint.Endpoint{
|
if len(targets) == 0 && virtualServer.Spec.VirtualServerAddress != "" {
|
||||||
Targets: endpoint.Targets{
|
targets = append(targets, virtualServer.Spec.VirtualServerAddress)
|
||||||
virtualServer.Spec.VirtualServerAddress,
|
}
|
||||||
},
|
if len(targets) == 0 && virtualServer.Status.VSAddress != "" {
|
||||||
RecordType: "A",
|
targets = append(targets, virtualServer.Status.VSAddress)
|
||||||
DNSName: virtualServer.Spec.Host,
|
|
||||||
Labels: endpoint.NewLabels(),
|
|
||||||
RecordTTL: ttl,
|
|
||||||
}
|
|
||||||
|
|
||||||
vs.setResourceLabel(virtualServer, ep)
|
|
||||||
endpoints = append(endpoints, ep)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if virtualServer.Status.VSAddress != "" {
|
resource := fmt.Sprintf("f5-virtualserver/%s/%s", virtualServer.Namespace, virtualServer.Name)
|
||||||
ep := &endpoint.Endpoint{
|
endpoints = append(endpoints, endpointsForHostname(virtualServer.Spec.Host, targets, ttl, nil, "", resource)...)
|
||||||
Targets: endpoint.Targets{
|
|
||||||
virtualServer.Status.VSAddress,
|
|
||||||
},
|
|
||||||
RecordType: "A",
|
|
||||||
DNSName: virtualServer.Spec.Host,
|
|
||||||
Labels: endpoint.NewLabels(),
|
|
||||||
RecordTTL: ttl,
|
|
||||||
}
|
|
||||||
|
|
||||||
vs.setResourceLabel(virtualServer, ep)
|
|
||||||
endpoints = append(endpoints, ep)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
@ -234,7 +213,3 @@ func (vs *f5VirtualServerSource) filterByAnnotations(virtualServers []*f5.Virtua
|
|||||||
|
|
||||||
return filteredList, nil
|
return filteredList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vs *f5VirtualServerSource) setResourceLabel(virtualServer *f5.VirtualServer, ep *endpoint.Endpoint) {
|
|
||||||
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("f5-virtualserver/%s/%s", virtualServer.Namespace, virtualServer.Name)
|
|
||||||
}
|
|
||||||
|
@ -44,6 +44,41 @@ func TestF5VirtualServerEndpoints(t *testing.T) {
|
|||||||
virtualServer f5.VirtualServer
|
virtualServer f5.VirtualServer
|
||||||
expected []*endpoint.Endpoint
|
expected []*endpoint.Endpoint
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "F5 VirtualServer with target annotation",
|
||||||
|
annotationFilter: "",
|
||||||
|
virtualServer: f5.VirtualServer{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: f5VirtualServerGVR.GroupVersion().String(),
|
||||||
|
Kind: "VirtualServer",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-vs",
|
||||||
|
Namespace: defaultF5VirtualServerNamespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
targetAnnotationKey: "192.168.1.150",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: f5.VirtualServerSpec{
|
||||||
|
Host: "www.example.com",
|
||||||
|
VirtualServerAddress: "192.168.1.100",
|
||||||
|
},
|
||||||
|
Status: f5.VirtualServerStatus{
|
||||||
|
VSAddress: "192.168.1.200",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "www.example.com",
|
||||||
|
Targets: []string{"192.168.1.150"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: 0,
|
||||||
|
Labels: endpoint.Labels{
|
||||||
|
"resource": "f5-virtualserver/virtualserver/test-vs",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "F5 VirtualServer with host and virtualServerAddress set",
|
name: "F5 VirtualServer with host and virtualServerAddress set",
|
||||||
annotationFilter: "",
|
annotationFilter: "",
|
||||||
@ -60,6 +95,9 @@ func TestF5VirtualServerEndpoints(t *testing.T) {
|
|||||||
Host: "www.example.com",
|
Host: "www.example.com",
|
||||||
VirtualServerAddress: "192.168.1.100",
|
VirtualServerAddress: "192.168.1.100",
|
||||||
},
|
},
|
||||||
|
Status: f5.VirtualServerStatus{
|
||||||
|
VSAddress: "192.168.1.200",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
expected: []*endpoint.Endpoint{
|
expected: []*endpoint.Endpoint{
|
||||||
{
|
{
|
||||||
|
@ -132,11 +132,16 @@ func (gs *glooSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("Gloo: Find %s proxy", proxy.Metadata.Name)
|
log.Debugf("Gloo: Find %s proxy", proxy.Metadata.Name)
|
||||||
proxyTargets, err := gs.proxyTargets(ctx, proxy.Metadata.Name, ns)
|
|
||||||
if err != nil {
|
proxyTargets := getTargetsFromTargetAnnotation(proxy.Metadata.Annotations)
|
||||||
return nil, err
|
if len(proxyTargets) == 0 {
|
||||||
|
proxyTargets, err = gs.proxyTargets(ctx, proxy.Metadata.Name, ns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Debugf("Gloo[%s]: Find %d target(s) (%+v)", proxy.Metadata.Name, len(proxyTargets), proxyTargets)
|
log.Debugf("Gloo[%s]: Find %d target(s) (%+v)", proxy.Metadata.Name, len(proxyTargets), proxyTargets)
|
||||||
|
|
||||||
proxyEndpoints, err := gs.generateEndpointsFromProxy(ctx, &proxy, proxyTargets)
|
proxyEndpoints, err := gs.generateEndpointsFromProxy(ctx, &proxy, proxyTargets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -302,6 +302,96 @@ var proxyMetadataStaticSource = metav1.PartialObjectMetadata{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Proxy with target annotation test
|
||||||
|
var targetAnnotatedProxy = proxy{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: proxyGVR.GroupVersion().String(),
|
||||||
|
Kind: "Proxy",
|
||||||
|
},
|
||||||
|
Metadata: metav1.ObjectMeta{
|
||||||
|
Name: "target-ann",
|
||||||
|
Namespace: defaultGlooNamespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"external-dns.alpha.kubernetes.io/target": "203.2.45.7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: proxySpec{
|
||||||
|
Listeners: []proxySpecListener{
|
||||||
|
{
|
||||||
|
HTTPListener: proxySpecHTTPListener{
|
||||||
|
VirtualHosts: []proxyVirtualHost{
|
||||||
|
{
|
||||||
|
Domains: []string{"i.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.Unknown",
|
||||||
|
Name: "my-unknown-svc",
|
||||||
|
Namespace: "unknown",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"j.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.VirtualService",
|
||||||
|
Name: "my-annotated-svc",
|
||||||
|
Namespace: "internal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetAnnotatedProxySvc = corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: targetAnnotatedProxy.Metadata.Name,
|
||||||
|
Namespace: targetAnnotatedProxy.Metadata.Namespace,
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
},
|
||||||
|
Status: corev1.ServiceStatus{
|
||||||
|
LoadBalancer: corev1.LoadBalancerStatus{
|
||||||
|
Ingress: []corev1.LoadBalancerIngress{
|
||||||
|
{
|
||||||
|
IP: "203.1.115.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "203.1.115.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
IP: "203.1.115.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetAnnotatedProxySource = metav1.PartialObjectMetadata{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: virtualServiceGVR.GroupVersion().String(),
|
||||||
|
Kind: "VirtualService",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: targetAnnotatedProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Name,
|
||||||
|
Namespace: targetAnnotatedProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Namespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"external-dns.alpha.kubernetes.io/ttl": "460",
|
||||||
|
"external-dns.alpha.kubernetes.io/aws-geolocation-country-code": "IT",
|
||||||
|
"external-dns.alpha.kubernetes.io/set-identifier": "identifier-annotated",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func TestGlooSource(t *testing.T) {
|
func TestGlooSource(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@ -318,10 +408,12 @@ func TestGlooSource(t *testing.T) {
|
|||||||
internalProxyUnstructured := unstructured.Unstructured{}
|
internalProxyUnstructured := unstructured.Unstructured{}
|
||||||
externalProxyUnstructured := unstructured.Unstructured{}
|
externalProxyUnstructured := unstructured.Unstructured{}
|
||||||
proxyMetadataStaticUnstructured := unstructured.Unstructured{}
|
proxyMetadataStaticUnstructured := unstructured.Unstructured{}
|
||||||
|
targetAnnotatedProxyUnstructured := unstructured.Unstructured{}
|
||||||
|
|
||||||
internalProxySourceUnstructured := unstructured.Unstructured{}
|
internalProxySourceUnstructured := unstructured.Unstructured{}
|
||||||
externalProxySourceUnstructured := unstructured.Unstructured{}
|
externalProxySourceUnstructured := unstructured.Unstructured{}
|
||||||
proxyMetadataStaticSourceUnstructured := unstructured.Unstructured{}
|
proxyMetadataStaticSourceUnstructured := unstructured.Unstructured{}
|
||||||
|
targetAnnotatedProxySourceUnstructured := unstructured.Unstructured{}
|
||||||
|
|
||||||
internalProxyAsJSON, err := json.Marshal(internalProxy)
|
internalProxyAsJSON, err := json.Marshal(internalProxy)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -332,6 +424,9 @@ func TestGlooSource(t *testing.T) {
|
|||||||
proxyMetadataStaticAsJSON, err := json.Marshal(proxyMetadataStatic)
|
proxyMetadataStaticAsJSON, err := json.Marshal(proxyMetadataStatic)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
targetAnnotatedProxyAsJSON, err := json.Marshal(targetAnnotatedProxy)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
internalProxySvcAsJSON, err := json.Marshal(internalProxySource)
|
internalProxySvcAsJSON, err := json.Marshal(internalProxySource)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
@ -341,13 +436,18 @@ func TestGlooSource(t *testing.T) {
|
|||||||
proxyMetadataStaticSvcAsJSON, err := json.Marshal(proxyMetadataStaticSource)
|
proxyMetadataStaticSvcAsJSON, err := json.Marshal(proxyMetadataStaticSource)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
targetAnnotatedProxySvcAsJSON, err := json.Marshal(targetAnnotatedProxySource)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.NoError(t, internalProxyUnstructured.UnmarshalJSON(internalProxyAsJSON))
|
assert.NoError(t, internalProxyUnstructured.UnmarshalJSON(internalProxyAsJSON))
|
||||||
assert.NoError(t, externalProxyUnstructured.UnmarshalJSON(externalProxyAsJSON))
|
assert.NoError(t, externalProxyUnstructured.UnmarshalJSON(externalProxyAsJSON))
|
||||||
assert.NoError(t, proxyMetadataStaticUnstructured.UnmarshalJSON(proxyMetadataStaticAsJSON))
|
assert.NoError(t, proxyMetadataStaticUnstructured.UnmarshalJSON(proxyMetadataStaticAsJSON))
|
||||||
|
assert.NoError(t, targetAnnotatedProxyUnstructured.UnmarshalJSON(targetAnnotatedProxyAsJSON))
|
||||||
|
|
||||||
assert.NoError(t, internalProxySourceUnstructured.UnmarshalJSON(internalProxySvcAsJSON))
|
assert.NoError(t, internalProxySourceUnstructured.UnmarshalJSON(internalProxySvcAsJSON))
|
||||||
assert.NoError(t, externalProxySourceUnstructured.UnmarshalJSON(externalProxySvcAsJSON))
|
assert.NoError(t, externalProxySourceUnstructured.UnmarshalJSON(externalProxySvcAsJSON))
|
||||||
assert.NoError(t, proxyMetadataStaticSourceUnstructured.UnmarshalJSON(proxyMetadataStaticSvcAsJSON))
|
assert.NoError(t, proxyMetadataStaticSourceUnstructured.UnmarshalJSON(proxyMetadataStaticSvcAsJSON))
|
||||||
|
assert.NoError(t, targetAnnotatedProxySourceUnstructured.UnmarshalJSON(targetAnnotatedProxySvcAsJSON))
|
||||||
|
|
||||||
// Create proxy resources
|
// Create proxy resources
|
||||||
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &internalProxyUnstructured, metav1.CreateOptions{})
|
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &internalProxyUnstructured, metav1.CreateOptions{})
|
||||||
@ -356,6 +456,8 @@ func TestGlooSource(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &proxyMetadataStaticUnstructured, metav1.CreateOptions{})
|
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &proxyMetadataStaticUnstructured, metav1.CreateOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &targetAnnotatedProxyUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Create proxy source
|
// Create proxy source
|
||||||
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(internalProxySource.Namespace).Create(context.Background(), &internalProxySourceUnstructured, metav1.CreateOptions{})
|
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(internalProxySource.Namespace).Create(context.Background(), &internalProxySourceUnstructured, metav1.CreateOptions{})
|
||||||
@ -364,6 +466,8 @@ func TestGlooSource(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(proxyMetadataStaticSource.Namespace).Create(context.Background(), &proxyMetadataStaticSourceUnstructured, metav1.CreateOptions{})
|
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(proxyMetadataStaticSource.Namespace).Create(context.Background(), &proxyMetadataStaticSourceUnstructured, metav1.CreateOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(targetAnnotatedProxySource.Namespace).Create(context.Background(), &targetAnnotatedProxySourceUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Create proxy service resources
|
// Create proxy service resources
|
||||||
_, err = fakeKubernetesClient.CoreV1().Services(internalProxySvc.GetNamespace()).Create(context.Background(), &internalProxySvc, metav1.CreateOptions{})
|
_, err = fakeKubernetesClient.CoreV1().Services(internalProxySvc.GetNamespace()).Create(context.Background(), &internalProxySvc, metav1.CreateOptions{})
|
||||||
@ -372,10 +476,12 @@ func TestGlooSource(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = fakeKubernetesClient.CoreV1().Services(proxyMetadataStaticSvc.GetNamespace()).Create(context.Background(), &proxyMetadataStaticSvc, metav1.CreateOptions{})
|
_, err = fakeKubernetesClient.CoreV1().Services(proxyMetadataStaticSvc.GetNamespace()).Create(context.Background(), &proxyMetadataStaticSvc, metav1.CreateOptions{})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeKubernetesClient.CoreV1().Services(targetAnnotatedProxySvc.GetNamespace()).Create(context.Background(), &targetAnnotatedProxySvc, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
endpoints, err := source.Endpoints(context.Background())
|
endpoints, err := source.Endpoints(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, endpoints, 8)
|
assert.Len(t, endpoints, 10)
|
||||||
assert.ElementsMatch(t, endpoints, []*endpoint.Endpoint{
|
assert.ElementsMatch(t, endpoints, []*endpoint.Endpoint{
|
||||||
{
|
{
|
||||||
DNSName: "a.test",
|
DNSName: "a.test",
|
||||||
@ -459,5 +565,26 @@ func TestGlooSource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
DNSName: "i.test",
|
||||||
|
Targets: []string{"203.2.45.7"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "j.test",
|
||||||
|
Targets: []string{"203.2.45.7"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
SetIdentifier: "identifier-annotated",
|
||||||
|
RecordTTL: 460,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
endpoint.ProviderSpecificProperty{
|
||||||
|
Name: "aws/geolocation-country-code",
|
||||||
|
Value: "IT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -124,13 +124,15 @@ func (sc *kongTCPIngressSource) Endpoints(ctx context.Context) ([]*endpoint.Endp
|
|||||||
|
|
||||||
var endpoints []*endpoint.Endpoint
|
var endpoints []*endpoint.Endpoint
|
||||||
for _, tcpIngress := range tcpIngresses {
|
for _, tcpIngress := range tcpIngresses {
|
||||||
var targets endpoint.Targets
|
targets := getTargetsFromTargetAnnotation(tcpIngress.Annotations)
|
||||||
for _, lb := range tcpIngress.Status.LoadBalancer.Ingress {
|
if len(targets) == 0 {
|
||||||
if lb.IP != "" {
|
for _, lb := range tcpIngress.Status.LoadBalancer.Ingress {
|
||||||
targets = append(targets, lb.IP)
|
if lb.IP != "" {
|
||||||
}
|
targets = append(targets, lb.IP)
|
||||||
if lb.Hostname != "" {
|
}
|
||||||
targets = append(targets, lb.Hostname)
|
if lb.Hostname != "" {
|
||||||
|
targets = append(targets, lb.Hostname)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +220,65 @@ func TestKongTCPIngressEndpoints(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "TCPIngress with target annotation",
|
||||||
|
tcpProxy: TCPIngress{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kongGroupdVersionResource.GroupVersion().String(),
|
||||||
|
Kind: "TCPIngress",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "tcp-ingress-sni",
|
||||||
|
Namespace: defaultKongNamespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"kubernetes.io/ingress.class": "kong",
|
||||||
|
"external-dns.alpha.kubernetes.io/target": "203.2.45.7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: tcpIngressSpec{
|
||||||
|
Rules: []tcpIngressRule{
|
||||||
|
{
|
||||||
|
Port: 30002,
|
||||||
|
Host: "b.example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Port: 30003,
|
||||||
|
Host: "c.example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: tcpIngressStatus{
|
||||||
|
LoadBalancer: corev1.LoadBalancerStatus{
|
||||||
|
Ingress: []corev1.LoadBalancerIngress{
|
||||||
|
{
|
||||||
|
Hostname: "a123456769a314e71861a4303f06a3bd-1291189659.us-east-1.elb.amazonaws.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "b.example.com",
|
||||||
|
Targets: []string{"203.2.45.7"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: 0,
|
||||||
|
Labels: endpoint.Labels{
|
||||||
|
"resource": "tcpingress/kong/tcp-ingress-sni",
|
||||||
|
},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "c.example.com",
|
||||||
|
Targets: []string{"203.2.45.7"},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
Labels: endpoint.Labels{
|
||||||
|
"resource": "tcpingress/kong/tcp-ingress-sni",
|
||||||
|
},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
ti := ti
|
ti := ti
|
||||||
t.Run(ti.title, func(t *testing.T) {
|
t.Run(ti.title, func(t *testing.T) {
|
||||||
|
@ -130,9 +130,12 @@ func (ns *nodeSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, erro
|
|||||||
log.Debugf("not applying template for %s", node.Name)
|
log.Debugf("not applying template for %s", node.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err := ns.nodeAddresses(node)
|
addrs := getTargetsFromTargetAnnotation(node.Annotations)
|
||||||
if err != nil {
|
if len(addrs) == 0 {
|
||||||
return nil, fmt.Errorf("failed to get node address from %s: %w", node.Name, err)
|
addrs, err = ns.nodeAddresses(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get node address from %s: %w", node.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ep.Labels = endpoint.NewLabels()
|
ep.Labels = endpoint.NewLabels()
|
||||||
|
@ -205,6 +205,17 @@ func testNodeSourceEndpoints(t *testing.T) {
|
|||||||
nodeAddresses: []v1.NodeAddress{},
|
nodeAddresses: []v1.NodeAddress{},
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "node with target annotation",
|
||||||
|
nodeName: "node1.example.org",
|
||||||
|
nodeAddresses: []v1.NodeAddress{{Type: v1.NodeExternalIP, Address: "1.2.3.4"}},
|
||||||
|
annotations: map[string]string{
|
||||||
|
"external-dns.alpha.kubernetes.io/target": "203.2.45.7",
|
||||||
|
},
|
||||||
|
expected: []*endpoint.Endpoint{
|
||||||
|
{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"203.2.45.7"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "annotated node without annotation filter returns endpoint",
|
title: "annotated node without annotation filter returns endpoint",
|
||||||
nodeName: "node1",
|
nodeName: "node1",
|
||||||
|
@ -89,22 +89,36 @@ func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targets := getTargetsFromTargetAnnotation(pod.Annotations)
|
||||||
|
|
||||||
if domainAnnotation, ok := pod.Annotations[internalHostnameAnnotationKey]; ok {
|
if domainAnnotation, ok := pod.Annotations[internalHostnameAnnotationKey]; ok {
|
||||||
domainList := splitHostnameAnnotation(domainAnnotation)
|
domainList := splitHostnameAnnotation(domainAnnotation)
|
||||||
for _, domain := range domainList {
|
for _, domain := range domainList {
|
||||||
addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP)
|
if len(targets) == 0 {
|
||||||
|
addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP)
|
||||||
|
} else {
|
||||||
|
for _, target := range targets {
|
||||||
|
addToEndpointMap(endpointMap, domain, suitableType(target), target)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if domainAnnotation, ok := pod.Annotations[hostnameAnnotationKey]; ok {
|
if domainAnnotation, ok := pod.Annotations[hostnameAnnotationKey]; ok {
|
||||||
domainList := splitHostnameAnnotation(domainAnnotation)
|
domainList := splitHostnameAnnotation(domainAnnotation)
|
||||||
for _, domain := range domainList {
|
for _, domain := range domainList {
|
||||||
node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName)
|
if len(targets) == 0 {
|
||||||
for _, address := range node.Status.Addresses {
|
node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName)
|
||||||
recordType := suitableType(address.Address)
|
for _, address := range node.Status.Addresses {
|
||||||
// IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well.
|
recordType := suitableType(address.Address)
|
||||||
if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) {
|
// IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well.
|
||||||
addToEndpointMap(endpointMap, domain, recordType, address.Address)
|
if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) {
|
||||||
|
addToEndpointMap(endpointMap, domain, recordType, address.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, target := range targets {
|
||||||
|
addToEndpointMap(endpointMap, domain, suitableType(target), target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,6 +316,78 @@ func TestPodSource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"create records based on pod's target annotation",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
[]*endpoint.Endpoint{
|
||||||
|
{DNSName: "a.foo.example.org", Targets: endpoint.Targets{"208.1.2.1", "208.1.2.2"}, RecordType: endpoint.RecordTypeA},
|
||||||
|
{DNSName: "internal.a.foo.example.org", Targets: endpoint.Targets{"208.1.2.1", "208.1.2.2"}, RecordType: endpoint.RecordTypeA},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
[]*corev1.Node{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-node1",
|
||||||
|
},
|
||||||
|
Status: corev1.NodeStatus{
|
||||||
|
Addresses: []corev1.NodeAddress{
|
||||||
|
{Type: corev1.NodeExternalIP, Address: "54.10.11.1"},
|
||||||
|
{Type: corev1.NodeInternalIP, Address: "10.0.1.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-node2",
|
||||||
|
},
|
||||||
|
Status: corev1.NodeStatus{
|
||||||
|
Addresses: []corev1.NodeAddress{
|
||||||
|
{Type: corev1.NodeExternalIP, Address: "54.10.11.2"},
|
||||||
|
{Type: corev1.NodeInternalIP, Address: "10.0.1.2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*corev1.Pod{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-pod1",
|
||||||
|
Namespace: "kube-system",
|
||||||
|
Annotations: map[string]string{
|
||||||
|
internalHostnameAnnotationKey: "internal.a.foo.example.org",
|
||||||
|
hostnameAnnotationKey: "a.foo.example.org",
|
||||||
|
targetAnnotationKey: "208.1.2.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
NodeName: "my-node1",
|
||||||
|
},
|
||||||
|
Status: corev1.PodStatus{
|
||||||
|
PodIP: "10.0.1.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-pod2",
|
||||||
|
Namespace: "kube-system",
|
||||||
|
Annotations: map[string]string{
|
||||||
|
internalHostnameAnnotationKey: "internal.a.foo.example.org",
|
||||||
|
hostnameAnnotationKey: "a.foo.example.org",
|
||||||
|
targetAnnotationKey: "208.1.2.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
NodeName: "my-node2",
|
||||||
|
},
|
||||||
|
Status: corev1.PodStatus{
|
||||||
|
PodIP: "10.0.1.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"create multiple records",
|
"create multiple records",
|
||||||
"",
|
"",
|
||||||
|
Loading…
Reference in New Issue
Block a user