mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
Merge remote-tracking branch 'origin/master' into ingress-class-filtering
This commit is contained in:
commit
097df5c458
3
.github/workflows/lint-test-chart.yaml
vendored
3
.github/workflows/lint-test-chart.yaml
vendored
@ -7,6 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
lint-test:
|
||||
if: github.repository == 'kubernetes-sigs/external-dns'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -36,7 +37,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint
|
||||
run: ct lint --check-version-increment=false
|
||||
|
||||
- name: Create Kind cluster
|
||||
uses: helm/kind-action@v1.2.0
|
||||
|
3
.github/workflows/release-chart.yaml
vendored
3
.github/workflows/release-chart.yaml
vendored
@ -5,10 +5,11 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "charts/external-dns/**"
|
||||
- "charts/external-dns/Chart.yaml"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository == 'kubernetes-sigs/external-dns'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
16
.github/workflows/trivy.yml
vendored
16
.github/workflows/trivy.yml
vendored
@ -1,8 +1,6 @@
|
||||
name: trivy vulnerability scanner
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
@ -10,18 +8,10 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build an image from Dockerfile
|
||||
run: |
|
||||
make build.docker
|
||||
|
||||
- uses: cachix/install-nix-action@v13
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: workflow/nix-shell-action@v1
|
||||
with:
|
||||
packages: trivy
|
||||
script: |
|
||||
make build.docker
|
||||
./scripts/run-trivy.sh
|
||||
- name: Run trivy
|
||||
run: |
|
||||
./scripts/run-trivy.sh
|
||||
|
||||
|
1
OWNERS
1
OWNERS
@ -4,6 +4,7 @@
|
||||
approvers:
|
||||
- raffo
|
||||
- njuettner
|
||||
- seanmalloy
|
||||
|
||||
reviewers:
|
||||
- njuettner
|
||||
|
@ -110,6 +110,13 @@ The following table clarifies the current status of the providers according to t
|
||||
| GoDaddy | Alpha | |
|
||||
| Gandi | Alpha | @packi |
|
||||
|
||||
## Kubernetes version compatibility
|
||||
|
||||
| ExternalDNS | <= 0.9.x | >= 0.10.0 |
|
||||
| ------------------ | :----------------: | :----------------: |
|
||||
| Kubernetes <= 1.18 | :white_check_mark: | :x: |
|
||||
| Kubernetes >= 1.19 | :x: | :white_check_mark: |
|
||||
|
||||
## Running ExternalDNS:
|
||||
|
||||
The are two ways of running ExternalDNS:
|
||||
|
6
charts/OWNERS
Normal file
6
charts/OWNERS
Normal file
@ -0,0 +1,6 @@
|
||||
labels:
|
||||
- chart
|
||||
approvers:
|
||||
- stevehipwell
|
||||
reviewers:
|
||||
- stevehipwell
|
@ -2,8 +2,8 @@ apiVersion: v2
|
||||
name: external-dns
|
||||
description: ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.
|
||||
type: application
|
||||
version: 1.4.0
|
||||
appVersion: 0.10.1
|
||||
version: 1.7.0
|
||||
appVersion: 0.10.2
|
||||
keywords:
|
||||
- kubernetes
|
||||
- external-dns
|
||||
@ -17,5 +17,9 @@ maintainers:
|
||||
email: steve.hipwell@gmail.com
|
||||
annotations:
|
||||
artifacthub.io/changes: |
|
||||
- kind: added
|
||||
description: "Allow custom ClusterRole rules to be specified for sources without defaults."
|
||||
- kind: changed
|
||||
description: "Update image to v0.10.1"
|
||||
description: "Update ExternalDNS version to v0.10.2."
|
||||
- kind: changed
|
||||
description: "Set ClusterRole rules based more enabled sources."
|
||||
|
@ -21,7 +21,7 @@ helm upgrade --install external-dns/external-dns
|
||||
The following table lists the configurable parameters of the _ExternalDNS_ chart and their default values.
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
|-----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|
|
||||
| `image.repository` | Image repository. | `k8s.gcr.io/external-dns/external-dns` |
|
||||
| `image.tag` | Image tag, will override the default tag derived from the chart app version. | `""` |
|
||||
| `image.pullPolicy` | Image pull policy. | `IfNotPresent` |
|
||||
@ -32,6 +32,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
||||
| `serviceAccount.annotations` | Annotations to add to the service account. | `{}` |
|
||||
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the full name template. | `""` |
|
||||
| `rbac.create` | If `true`, create the RBAC resources. | `true` |
|
||||
| `rbac.additionalPermissions` | Additional permissions to be added to the cluster role. | `{}` |
|
||||
| `podLabels` | Labels to add to the pod. | `{}` |
|
||||
| `podAnnotations` | Annotations to add to the pod. | `{}` |
|
||||
| `podSecurityContext` | Security context for the pod, this supports the full [PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#podsecuritycontext-v1-core) API. | _see values.yaml_ |
|
||||
@ -45,6 +46,7 @@ The following table lists the configurable parameters of the _ExternalDNS_ chart
|
||||
| `env` | [Environment variables](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) for the _external-dns_ container, this supports the full [EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#envvar-v1-core) API including secrets and configmaps. | `[]` |
|
||||
| `livenessProbe` | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `readinessProbe` | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for the _external-dns_ container, this supports the full [Probe](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) API. | See _values.yaml_ |
|
||||
| `service.annotations` | Annotations to add to the service. | `{}` |
|
||||
| `service.port` | Port to expose via the service. | `7979` |
|
||||
| `extraVolumes` | Additional volumes for the pod, this supports the full [VolumeDevice](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumedevice-v1-core) API. | `[]` |
|
||||
| `extraVolumeMounts` | Additional volume mounts for the _external-dns_ container, this supports the full [VolumeMount](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#volumemount-v1-core) API. | `[]` |
|
||||
|
@ -6,13 +6,98 @@ metadata:
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- if or (has "node" .Values.sources) (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "pod" .Values.sources) (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "service" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "gloo-proxy" .Values.sources) (has "istio-gateway" .Values.sources) (has "istio-virtualservice" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if or (has "ingress" .Values.sources) (has "contour-httpproxy" .Values.sources) (has "openshift-route" .Values.sources) (has "skipper-routegroup" .Values.sources) }}
|
||||
- apiGroups: ["extensions","networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "istio-gateway" .Values.sources }}
|
||||
- apiGroups: ["networking.istio.io"]
|
||||
resources: ["gateways"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "istio-virtualservice" .Values.sources }}
|
||||
- apiGroups: ["networking.istio.io"]
|
||||
resources: ["virtualservices"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "ambassador-host" .Values.sources }}
|
||||
- apiGroups: ["getambassador.io"]
|
||||
resources: ["hosts","ingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "contour-httpproxy" .Values.sources }}
|
||||
- apiGroups: ["projectcontour.io"]
|
||||
resources: ["httpproxies"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "contour-ingressroute" .Values.sources }}
|
||||
- apiGroups: ["contour.heptio.com"]
|
||||
resources: ["ingressroutes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "crd" .Values.sources }}
|
||||
- apiGroups: ["externaldns.k8s.io"]
|
||||
resources: ["dnsendpoints"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["externaldns.k8s.io"]
|
||||
resources: ["dnsendpoints/status"]
|
||||
verbs: ["*"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "gloo-proxy" .Values.sources }}
|
||||
- apiGroups: ["gloo.solo.io","gateway.solo.io"]
|
||||
resources: ["proxies","virtualservices"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "kong-tcpingress" .Values.sources }}
|
||||
- apiGroups: ["configuration.konghq.com"]
|
||||
resources: ["tcpingresses"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "openshift-route" .Values.sources }}
|
||||
- apiGroups: ["route.openshift.io"]
|
||||
resources: ["routes"]
|
||||
verbs: ["get","watch","list"]
|
||||
{{- end }}
|
||||
|
||||
{{- if has "skipper-routegroup" .Values.sources }}
|
||||
- apiGroups: ["zalando.org"]
|
||||
resources: ["routegroups"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["zalando.org"]
|
||||
resources: ["routegroups/status"]
|
||||
verbs: ["patch","update"]
|
||||
{{- end }}
|
||||
|
||||
{{- with .Values.rbac.additionalPermissions }}
|
||||
{{- toYaml . | nindent 2 }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
|
@ -4,6 +4,10 @@ metadata:
|
||||
name: {{ include "external-dns.fullname" . }}
|
||||
labels:
|
||||
{{- include "external-dns.labels" . | nindent 4 }}
|
||||
{{- with .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
|
@ -24,6 +24,7 @@ serviceAccount:
|
||||
rbac:
|
||||
# Specifies whether RBAC resources should be created
|
||||
create: true
|
||||
additionalPermissions: {}
|
||||
|
||||
podLabels: {}
|
||||
|
||||
@ -73,6 +74,7 @@ readinessProbe:
|
||||
|
||||
service:
|
||||
port: 7979
|
||||
annotations: {}
|
||||
|
||||
extraVolumes: []
|
||||
|
||||
|
@ -3,7 +3,7 @@ timeout: 5000s
|
||||
options:
|
||||
substitution_option: ALLOW_LOOSE
|
||||
steps:
|
||||
- name: "gcr.io/k8s-testimages/gcb-docker-gcloud:v20200824-5d057db"
|
||||
- name: "gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90"
|
||||
entrypoint: make
|
||||
env:
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
@ -94,6 +94,30 @@ var (
|
||||
Help: "Number of Source errors.",
|
||||
},
|
||||
)
|
||||
registryARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "registry",
|
||||
Name: "a_records",
|
||||
Help: "Number of Registry A records.",
|
||||
},
|
||||
)
|
||||
sourceARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "source",
|
||||
Name: "a_records",
|
||||
Help: "Number of Source A records.",
|
||||
},
|
||||
)
|
||||
verifiedARecords = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "external_dns",
|
||||
Subsystem: "controller",
|
||||
Name: "verified_a_records",
|
||||
Help: "Number of DNS A-records that exists both in source and registry.",
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -105,6 +129,9 @@ func init() {
|
||||
prometheus.MustRegister(deprecatedRegistryErrors)
|
||||
prometheus.MustRegister(deprecatedSourceErrors)
|
||||
prometheus.MustRegister(controllerNoChangesTotal)
|
||||
prometheus.MustRegister(registryARecords)
|
||||
prometheus.MustRegister(sourceARecords)
|
||||
prometheus.MustRegister(verifiedARecords)
|
||||
}
|
||||
|
||||
// Controller is responsible for orchestrating the different components.
|
||||
@ -141,7 +168,8 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
registryEndpointsTotal.Set(float64(len(records)))
|
||||
|
||||
regARecords := filterARecords(records)
|
||||
registryARecords.Set(float64(len(regARecords)))
|
||||
ctx = context.WithValue(ctx, provider.RecordsContextKey, records)
|
||||
|
||||
endpoints, err := c.Source.Endpoints(ctx)
|
||||
@ -151,7 +179,10 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
sourceEndpointsTotal.Set(float64(len(endpoints)))
|
||||
|
||||
srcARecords := filterARecords(endpoints)
|
||||
sourceARecords.Set(float64(len(srcARecords)))
|
||||
vRecords := fetchMatchingARecords(endpoints, records)
|
||||
verifiedARecords.Set(float64(len(vRecords)))
|
||||
endpoints = c.Registry.AdjustEndpoints(endpoints)
|
||||
|
||||
plan := &plan.Plan{
|
||||
@ -181,6 +212,32 @@ func (c *Controller) RunOnce(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks and returns the intersection of A records in endpoint and registry.
|
||||
func fetchMatchingARecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) []string {
|
||||
aRecords := filterARecords(endpoints)
|
||||
recordsMap := make(map[string]struct{})
|
||||
for _, regRecord := range registryRecords {
|
||||
recordsMap[regRecord.DNSName] = struct{}{}
|
||||
}
|
||||
var cm []string
|
||||
for _, sourceRecord := range aRecords {
|
||||
if _, found := recordsMap[sourceRecord]; found {
|
||||
cm = append(cm, sourceRecord)
|
||||
}
|
||||
}
|
||||
return cm
|
||||
}
|
||||
|
||||
func filterARecords(endpoints []*endpoint.Endpoint) []string {
|
||||
var aRecords []string
|
||||
for _, endPoint := range endpoints {
|
||||
if endPoint.RecordType == endpoint.RecordTypeA {
|
||||
aRecords = append(aRecords, endPoint.DNSName)
|
||||
}
|
||||
}
|
||||
return aRecords
|
||||
}
|
||||
|
||||
// ScheduleRunOnce makes sure execution happens at most once per interval.
|
||||
func (c *Controller) ScheduleRunOnce(now time.Time) {
|
||||
c.nextRunAtMux.Lock()
|
||||
|
@ -19,6 +19,8 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@ -49,6 +51,10 @@ type filteredMockProvider struct {
|
||||
ApplyChangesCalls []*plan.Changes
|
||||
}
|
||||
|
||||
type errorMockProvider struct {
|
||||
mockProvider
|
||||
}
|
||||
|
||||
func (p *filteredMockProvider) GetDomainFilter() endpoint.DomainFilterInterface {
|
||||
return p.domainFilter
|
||||
}
|
||||
@ -70,6 +76,10 @@ func (p *mockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error
|
||||
return p.RecordsStore, nil
|
||||
}
|
||||
|
||||
func (p *errorMockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
return nil, errors.New("error for testing")
|
||||
}
|
||||
|
||||
// ApplyChanges validates that the passed in changes satisfy the assumptions.
|
||||
func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
if len(changes.Create) != len(p.ExpectChanges.Create) {
|
||||
@ -180,6 +190,13 @@ func TestRunOnce(t *testing.T) {
|
||||
|
||||
// Validate that the mock source was called.
|
||||
source.AssertExpectations(t)
|
||||
// check the verified records
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedARecords))
|
||||
}
|
||||
|
||||
func valueFromMetric(metric prometheus.Gauge) uint64 {
|
||||
ref := reflect.ValueOf(metric)
|
||||
return reflect.Indirect(ref).FieldByName("valBits").Uint()
|
||||
}
|
||||
|
||||
func TestShouldRunOnce(t *testing.T) {
|
||||
@ -376,3 +393,127 @@ func TestWhenMultipleControllerConsidersAllFilteredComain(t *testing.T) {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestVerifyARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
{
|
||||
DNSName: "create-record.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))
|
||||
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"24.24.24.24"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "some-record.2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "some-record.3.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"24.24.24.24"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))
|
||||
}
|
||||
|
||||
func TestARecords(t *testing.T) {
|
||||
testControllerFiltersDomains(
|
||||
t,
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
endpoint.NewDomainFilter([]string{"used.tld"}),
|
||||
[]*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record1.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"1.2.3.4"},
|
||||
},
|
||||
{
|
||||
DNSName: "_mysql-svc._tcp.mysql.used.tld",
|
||||
RecordType: endpoint.RecordTypeSRV,
|
||||
Targets: endpoint.Targets{"0 50 30007 mysql.used.tld"},
|
||||
},
|
||||
},
|
||||
[]*plan.Changes{{
|
||||
Create: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "record2.used.tld",
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
Targets: endpoint.Targets{"8.8.8.8"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
)
|
||||
assert.Equal(t, math.Float64bits(2), valueFromMetric(sourceARecords))
|
||||
assert.Equal(t, math.Float64bits(1), valueFromMetric(registryARecords))
|
||||
}
|
||||
|
5
docs/contributing/chart.md
Normal file
5
docs/contributing/chart.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Helm Chart
|
||||
|
||||
## Chart Changes
|
||||
|
||||
When contributing chart changes please follow the same process as when contributing other content but also please **DON'T** modify _Chart.yaml_ in the PR as this would result in a chart release when merged and will mean that your PR will need modifying before it can be accepted. The chart version will be updated as part of the PR to release the chart.
|
@ -185,6 +185,10 @@ Here is the full list of available metrics provided by ExternalDNS:
|
||||
| external_dns_registry_errors_total | Number of Registry errors | Counter |
|
||||
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
|
||||
| external_dns_source_errors_total | Number of Source errors | Counter |
|
||||
| external_dns_controller_verified_records | Number of DNS A-records that exists both in | Gauge |
|
||||
| | source & registry | |
|
||||
| external_dns_registry_a_records | Number of A records in registry | Gauge |
|
||||
| external_dns_source_a_records | Number of A records in source | Gauge |
|
||||
|
||||
### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects?
|
||||
|
||||
|
@ -24,10 +24,21 @@ You must be an official maintainer of the project to be able to do a release.
|
||||
|
||||
### Steps
|
||||
|
||||
- Run `scripts/releaser.sh` to create a new GitHub release.
|
||||
- Run `scripts/releaser.sh` to create a new GitHub release. Alternatively you can create a release in the GitHub UI making sure to click on the autogenerate release node feature.
|
||||
- The step above will trigger the Kubernetes based CI/CD system [Prow](https://prow.k8s.io/?repo=kubernetes-sigs%2Fexternal-dns). Verify that a new image was built and uploaded to `gcr.io/k8s-staging-external-dns/external-dns`.
|
||||
- Create a PR in the [k8s.io repo](https://github.com/kubernetes/k8s.io) (see https://github.com/kubernetes/k8s.io/pull/540 for reference) by taking the current staging image using the sha256 digest. Once the PR is merged, the image will be live with the corresponding tag specified in the PR.
|
||||
- Verify that the image is pullable with the given tag (i.e. `v0.7.5`).
|
||||
- Branch out from the default branch and run `scripts/kustomize-version-udapter.sh` to update the image tag used in the kustomization.yaml.
|
||||
- Create an issue to release the corresponding Helm chart via the chart release process (below) assigned to a chart maintainer
|
||||
- Create a PR with the kustomize change.
|
||||
- Once the PR is merged, all is done :-)
|
||||
|
||||
## How to release a new chart version
|
||||
|
||||
The chart needs to be released in response to an ExternalDNS image release or on an as-needed basis; this should be triggered by an issue to release the chart.
|
||||
|
||||
### Steps
|
||||
|
||||
- Create a PR to update _Chart.yaml_ with the ExternalDNS version in `appVersion`, agreed on chart release version in `version` and `annotations` showing the changes
|
||||
- Validate that the chart linting is successful
|
||||
- Merge the PR to trigger a GitHub action to release the chart
|
||||
|
@ -83,14 +83,18 @@ metadata:
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
rules:
|
||||
- host: echoserver.mycluster.example.org
|
||||
http: &echoserver_root
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
- host: echoserver.example.org
|
||||
http: *echoserver_root
|
||||
```
|
||||
@ -119,13 +123,17 @@ metadata:
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
In the above example we create a default path that works for any hostname, and
|
||||
@ -154,14 +162,18 @@ metadata:
|
||||
kubernetes.io/ingress.class: alb
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: alb
|
||||
rules:
|
||||
- host: echoserver.example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
The above Ingress object will result in the creation of an ALB with a dualstack
|
||||
|
@ -55,7 +55,7 @@ If your EKS-managed cluster is >= 1.13 and was created after 2019-09-04, refer
|
||||
to the [Amazon EKS
|
||||
documentation](https://docs.aws.amazon.com/eks/latest/userguide/create-service-account-iam-policy-and-role.html)
|
||||
for instructions on how to create the IAM Role. Otherwise, you will need to use
|
||||
kiam or kube2iam.
|
||||
kiam or kube2iam or set the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY on the deployment.
|
||||
|
||||
### kiam
|
||||
|
||||
@ -464,6 +464,58 @@ $ aws route53 delete-hosted-zone --id /hostedzone/ZEWFWZ4R16P7IB
|
||||
## Throttling
|
||||
|
||||
Route53 has a [5 API requests per second per account hard quota](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests-route-53).
|
||||
Running several fast polling ExternalDNS instances in a given account can easily hit that limit. Some ways to circumvent that issue includes:
|
||||
* Augment the synchronization interval (`--interval`), at the cost of slower changes propagation.
|
||||
* If the ExternalDNS managed zones list doesn't change frequently, set `--aws-zones-cache-duration` (zones list cache time-to-live) to a larger value. Note that zones list cache can be disabled with `--aws-zones-cache-duration=0s`.
|
||||
Running several fast polling ExternalDNS instances in a given account can easily hit that limit. Some ways to reduce the request rate include:
|
||||
* Reduce the polling loop's synchronization interval at the possible cost of slower change propagation (but see `--events` below to reduce the impact).
|
||||
* `--interval=5m` (default `1m`)
|
||||
* Trigger the polling loop on changes to K8s objects, rather than only at `interval`, to have responsive updates with long poll intervals
|
||||
* `--events`
|
||||
* Limit the [sources watched](https://github.com/kubernetes-sigs/external-dns/blob/master/pkg/apis/externaldns/types.go#L364) when the `--events` flag is specified to specific types, namespaces, labels, or annotations
|
||||
* `--source=ingress --source=service` - specify multiple times for multiple sources
|
||||
* `--namespace=my-app`
|
||||
* `--label-filter=app in (my-app)`
|
||||
* `--annotation-filter=kubernetes.io/ingress.class in (nginx-external)` - note that this filter would apply to services too..
|
||||
* Limit services watched by type (not applicable to ingress or other types)
|
||||
* `--service-type-filter=LoadBalancer` default `all`
|
||||
* Limit the hosted zones considered
|
||||
* `--zone-id-filter=ABCDEF12345678` - specify multiple times if needed
|
||||
* `--domain-filter=example.com` by domain suffix - specify multiple times if needed
|
||||
* `--regex-domain-filter=example*` by domain suffix but as a regex - overrides domain-filter
|
||||
* `--exclude-domains=ignore.this.example.com` to exclude a domain or subdomain
|
||||
* `--regex-domain-exclusion=ignore*` subtracts it's matches from `regex-domain-filter`'s matches
|
||||
* `--aws-zone-type=public` only sync zones of this type `[public|private]`
|
||||
* `--aws-zone-tags=owner=k8s` only sync zones with this tag
|
||||
* If the list of zones managed by ExternalDNS doesn't change frequently, cache it by setting a TTL.
|
||||
* `--aws-zones-cache-duration=3h` (default `0` - disabled)
|
||||
* Increase the number of changes applied to Route53 in each batch
|
||||
* `--aws-batch-change-size=4000` (default `1000`)
|
||||
* Increase the interval between changes
|
||||
* `--aws-batch-change-interval=10s` (default `1s`)
|
||||
* Introducing some jitter to the pod initialization, so that when multiple instances of ExternalDNS are updated at the same time they do not make their requests on the same second.
|
||||
|
||||
A simple way to implement randomised startup is with an init container:
|
||||
|
||||
```
|
||||
...
|
||||
spec:
|
||||
initContainers:
|
||||
- name: init-jitter
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- 'FOR=$((RANDOM % 10))s;echo "Sleeping for $FOR";sleep $FOR'
|
||||
containers:
|
||||
...
|
||||
```
|
||||
|
||||
### EKS
|
||||
|
||||
An effective starting point for EKS with an ingress controller might look like:
|
||||
|
||||
```bash
|
||||
--interval=5m
|
||||
--events
|
||||
--source=ingress
|
||||
--domain-filter=example.com
|
||||
--aws-zones-cache-duration=1h
|
||||
```
|
||||
|
@ -20,6 +20,10 @@ BlueCat Gateway username and password can be supplied using the configuration fi
|
||||
| rootZone | Yes |
|
||||
| skipTLSVerify | No (default false) |
|
||||
|
||||
### HTTP proxy
|
||||
|
||||
BlueCat provider supports getting the proxy URL from the environment variables. The format is the one specified by golang's [http.ProxyFromEnvironment](https://pkg.go.dev/net/http#ProxyFromEnvironment).
|
||||
|
||||
## Deploy
|
||||
Setup configuration file as k8s `Secret`.
|
||||
```
|
||||
|
@ -145,14 +145,18 @@ metadata:
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
rules:
|
||||
- host: echoserver.mycluster.example.org
|
||||
http: &echoserver_root
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
- host: echoserver.example.org
|
||||
http: *echoserver_root
|
||||
```
|
||||
@ -180,13 +184,17 @@ metadata:
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
In the above example we create a default path that works for any hostname, and
|
||||
@ -213,14 +221,18 @@ metadata:
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
rules:
|
||||
- host: echoserver.example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
The above Ingress object will result in the creation of an ALB with a dualstack
|
||||
@ -247,14 +259,18 @@ metadata:
|
||||
kubernetes.io/ingress.class: skipper
|
||||
name: echoserver
|
||||
spec:
|
||||
ingressClassName: skipper
|
||||
rules:
|
||||
- host: echoserver.example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: echoserver
|
||||
servicePort: 80
|
||||
path: /
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: echoserver
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
The above Ingress object will result in the creation of an NLB. A
|
||||
|
@ -297,13 +297,18 @@ metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: nginx
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
|
||||
---
|
||||
|
||||
@ -593,13 +598,18 @@ metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: via-ingress.external-dns-test.gcp.zalan.do
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: 80
|
||||
- path: /
|
||||
backend:
|
||||
service:
|
||||
name: nginx
|
||||
port:
|
||||
number: 80
|
||||
pathType: Prefix
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
@ -2,6 +2,60 @@
|
||||
This tutorial describes how to configure ExternalDNS to use the OpenShift Route source.
|
||||
It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
### For OCP 4.x
|
||||
|
||||
In OCP 4.x, if you have multiple ingress controllers then you must specify an ingress controller name or a router name(you can get it from the route's Status.Ingress.RouterName field).
|
||||
If you don't specify an ingress controller's or router name when you have multiple ingresscontrollers in your environment then the route gets populated with multiple entries of router canonical hostnames which causes external dns to create a CNAME record with multiple router canonical hostnames pointing to the route host which is a violation of RFC 1912 and is not allowed by Cloud Providers which leads to failure of record creation.
|
||||
Once you specify the ingresscontroller or router name then that will be matched by the external-dns and the router canonical hostname corresponding to this routerName(which is present in route's Status.Ingress.RouterName field) is selected and a CNAME record of this route host pointing to this router canonical hostname is created.
|
||||
|
||||
Your externaldns CR shall be created as per the following example.
|
||||
Replace names in the domain section and zone ID as per your environment.
|
||||
This is example is for AWS environment.
|
||||
|
||||
```yaml
|
||||
|
||||
apiVersion: externaldns.olm.openshift.io/v1alpha1
|
||||
kind: ExternalDNS
|
||||
metadata:
|
||||
name: sample1
|
||||
spec:
|
||||
domains:
|
||||
- filterType: Include
|
||||
matchType: Exact
|
||||
names: apps.miheer.externaldns
|
||||
provider:
|
||||
type: AWS
|
||||
source:
|
||||
hostnameAnnotation: Allow
|
||||
openshiftRouteOptions:
|
||||
routerName: default
|
||||
type: OpenShiftRoute
|
||||
zones:
|
||||
- Z05387772BD5723IZFRX3
|
||||
|
||||
```
|
||||
|
||||
This will create an externaldns pod with the following container args under spec in the external-dns namespace where `- --source=openshift-route` and `- --openshift-router-name=default` is added by the external-dns-operator.
|
||||
|
||||
```
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --domain-filter=apps.misalunk.externaldns
|
||||
- --metrics-address=127.0.0.1:7979
|
||||
- --txt-owner-id=external-dns-sample1
|
||||
- --provider=aws
|
||||
- --source=openshift-route
|
||||
- --policy=sync
|
||||
- --registry=txt
|
||||
- --log-level=debug
|
||||
- --zone-id-filter=Z05387772BD5723IZFRX3
|
||||
- --openshift-router-name=default
|
||||
- --txt-prefix=external-dns-
|
||||
|
||||
```
|
||||
|
||||
### For OCP 3.11 environment
|
||||
### Prepare ROUTER_CANONICAL_HOSTNAME in default/router deployment
|
||||
Read and go through [Finding the Host Name of the Router](https://docs.openshift.com/container-platform/3.11/install_config/router/default_haproxy_router.html#finding-router-hostname).
|
||||
If no ROUTER_CANONICAL_HOSTNAME is set, you must annotate each route with external-dns.alpha.kubernetes.io/target!
|
||||
|
8
go.mod
8
go.mod
@ -18,19 +18,19 @@ require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357
|
||||
github.com/aws/aws-sdk-go v1.40.53
|
||||
github.com/bodgit/tsig v0.0.2
|
||||
github.com/cloudflare/cloudflare-go v0.13.2
|
||||
github.com/cloudflare/cloudflare-go v0.25.0
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/datawire/ambassador v1.6.0
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba
|
||||
github.com/digitalocean/godo v1.69.1
|
||||
github.com/dnsimple/dnsimple-go v0.60.0
|
||||
github.com/exoscale/egoscale v0.73.2
|
||||
github.com/exoscale/egoscale v0.80.1
|
||||
github.com/ffledgling/pdns-go v0.0.0-20180219074714-524e7daccd99
|
||||
github.com/go-gandi/go-gandi v0.0.0-20200921091836-0d8a64b9cc09
|
||||
github.com/go-logr/logr v1.1.0 // indirect
|
||||
github.com/golang/sync v0.0.0-20180314180146-1d60e4601c6f
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/gophercloud/gophercloud v0.21.0
|
||||
github.com/gophercloud/gophercloud v0.22.0
|
||||
github.com/hooklift/gowsdl v0.5.0
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@ -48,7 +48,7 @@ require (
|
||||
github.com/oracle/oci-go-sdk v21.4.0+incompatible
|
||||
github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectcontour/contour v1.18.1
|
||||
github.com/projectcontour/contour v1.18.2
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
|
26
go.sum
26
go.sum
@ -126,9 +126,9 @@ github.com/Raffo/knolog v0.0.0-20211016155154-e4d5e0cc970a/go.mod h1:AsBYmtPY5rg
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/Venafi/vcert/v4 v4.13.1/go.mod h1:Z3sJFoAurFNXPpoSUSHq46aIeHLiGQEMDhprfxlpofQ=
|
||||
github.com/StackExchange/dnscontrol v0.2.8 h1:7jviqDH9cIqRSRpH0UxgmpT7a8CwEhs9mLHBhoYhXo8=
|
||||
github.com/StackExchange/dnscontrol v0.2.8/go.mod h1:BH+5nX50JxHDdb3+AD/z/UfYMCc7iaqEkRtQ+NjcFGE=
|
||||
github.com/Venafi/vcert/v4 v4.13.1/go.mod h1:Z3sJFoAurFNXPpoSUSHq46aIeHLiGQEMDhprfxlpofQ=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
@ -212,8 +212,13 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2 h1:bhMGoNhAg21DuqJjU9jQepRRft6vYfo6pejT3NN4V6A=
|
||||
github.com/cloudflare/cloudflare-go v0.10.1 h1:d2CL6F9k2O0Ux0w27LgogJ5UOzZRj6a/hDPFqPP68d8=
|
||||
github.com/cloudflare/cloudflare-go v0.10.1/go.mod h1:C0Y6eWnTJPMK2ceuOxx2pjh78UUHihcXeTTHb8r7QjU=
|
||||
github.com/cloudflare/cloudflare-go v0.13.2/go.mod h1:27kfc1apuifUmJhp069y0+hwlKDg4bd8LWlu7oKeZvM=
|
||||
github.com/cloudflare/cloudflare-go v0.22.0 h1:3TYbkyz/IBbM6XozrTKWiNpv7RjJGamALy9yu2SBqTo=
|
||||
github.com/cloudflare/cloudflare-go v0.22.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
|
||||
github.com/cloudflare/cloudflare-go v0.25.0 h1:GwyKwGq8ciGNjKiTpjj6RvU3+uJNuPBNjlUkeQRx0yU=
|
||||
github.com/cloudflare/cloudflare-go v0.25.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s=
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14=
|
||||
github.com/cncf/udpa v0.0.0-20200324003616-bae28a880fdb/go.mod h1:HNVadOiXCy7Jk3R2knJ+qm++zkncJxxBMpjdGgJ+UJc=
|
||||
@ -328,8 +333,8 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exoscale/egoscale v0.73.2 h1:YGy0YufwuaRduaqTM6ptyZQpBxr2L9eA6kKfmGW8jKg=
|
||||
github.com/exoscale/egoscale v0.73.2/go.mod h1:Fpy/cIVjiUkI0DntkoFl4fZpaeqRFQ6SVcX1A0APg0E=
|
||||
github.com/exoscale/egoscale v0.80.1 h1:+JR0RhGKjkgHIluWeRWTTnveSGIRE1ifbpnjB/Fkwlk=
|
||||
github.com/exoscale/egoscale v0.80.1/go.mod h1:wi0myUxPsV8SdEtdJHQJxFLL/wEw9fiw9Gs1PWRkvkM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
@ -571,8 +576,8 @@ github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gookit/color v1.2.3/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gophercloud/gophercloud v0.21.0 h1:21rxoQM7cSaZCPgfP45h71Vt1amZa942l7AtUWLOI2I=
|
||||
github.com/gophercloud/gophercloud v0.21.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
github.com/gophercloud/gophercloud v0.22.0 h1:9lFISNLafZcecT0xUveIMt3IafexC6DIV9ek1SZdSMw=
|
||||
github.com/gophercloud/gophercloud v0.22.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@ -732,7 +737,6 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d h1:JV46OtdhH2vVt8mJ1EWUE94k99vbN9fZs1WQ8kcEapU=
|
||||
github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d/go.mod h1:CHQ3o5KBH1PIS2Fb1mRLTIWO5YzP9kSUB3KoCICwlvA=
|
||||
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
@ -782,6 +786,7 @@ github.com/mattn/go-oci8 v0.0.7/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mN
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@ -858,6 +863,7 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.4.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -938,8 +944,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/projectcontour/contour v1.18.1 h1:J1MmqchDFuou066Gkr13eYtPaBnfb5j7h5ETH9OfIfQ=
|
||||
github.com/projectcontour/contour v1.18.1/go.mod h1:prL5IyPK2ek6MUWwm8C5S1pHsWOE/2Y8Ym7ak3feVpo=
|
||||
github.com/projectcontour/contour v1.18.2 h1:q16Q0f8mb14DATpCus/qBONawBy1Zn4vrvBj1bXh3hc=
|
||||
github.com/projectcontour/contour v1.18.2/go.mod h1:prL5IyPK2ek6MUWwm8C5S1pHsWOE/2Y8Ym7ak3feVpo=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
@ -1084,6 +1090,7 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
@ -1277,6 +1284,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
|
||||
|
@ -3,7 +3,7 @@ kind: Kustomization
|
||||
|
||||
images:
|
||||
- name: k8s.gcr.io/external-dns/external-dns
|
||||
newTag: v0.10.0
|
||||
newTag: v0.10.2
|
||||
|
||||
resources:
|
||||
- ./external-dns-deployment.yaml
|
||||
|
1
main.go
1
main.go
@ -132,6 +132,7 @@ func main() {
|
||||
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
||||
RequestTimeout: cfg.RequestTimeout,
|
||||
DefaultTargets: cfg.DefaultTargets,
|
||||
OCPRouterName: cfg.OCPRouterName,
|
||||
}
|
||||
|
||||
// Lookup all the selected sources by names and pass them the desired configuration.
|
||||
|
@ -176,6 +176,7 @@ type Config struct {
|
||||
GoDaddySecretKey string `secure:"yes"`
|
||||
GoDaddyTTL int64
|
||||
GoDaddyOTE bool
|
||||
OCPRouterName string
|
||||
}
|
||||
|
||||
var defaultConfig = &Config{
|
||||
@ -363,8 +364,9 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
// Flags related to Skipper RouteGroup
|
||||
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
|
||||
|
||||
// Flags related to processing sources
|
||||
// Flags related to processing source
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress")
|
||||
app.Flag("openshift-router-name", "if source is openshift-route then you can pass the ingress controller name. Based on this name external-dns will select the respective router from the route status and map that routerCanonicalHostname to the route host while creating a CNAME record.").StringVar(&cfg.OCPRouterName)
|
||||
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
|
||||
app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)
|
||||
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently supported by source types CRD, ingress, service and openshift-route").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
||||
|
@ -115,6 +115,7 @@ var (
|
||||
DigitalOceanAPIPageSize: 50,
|
||||
ManagedDNSRecordTypes: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
RFC2136BatchChangeSize: 50,
|
||||
OCPRouterName: "default",
|
||||
}
|
||||
|
||||
overriddenConfig = &Config{
|
||||
@ -225,6 +226,7 @@ func TestParseFlags(t *testing.T) {
|
||||
args: []string{
|
||||
"--source=service",
|
||||
"--provider=google",
|
||||
"--openshift-router-name=default",
|
||||
},
|
||||
envVars: map[string]string{},
|
||||
expected: minimalConfig,
|
||||
|
@ -353,6 +353,7 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"),
|
||||
endpoint.NewEndpoint("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"),
|
||||
})
|
||||
@ -376,6 +377,7 @@ func TestAWSRecords(t *testing.T) {
|
||||
endpoint.NewEndpointWithTTL("multi-value-answer-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set").WithProviderSpecific(providerSpecificMultiValueAnswer, ""),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationContinentCode, "EU"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificGeolocationCountryCode, "DE"),
|
||||
endpoint.NewEndpointWithTTL("geolocation-subdivision-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "1.2.3.4").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificGeolocationSubdivisionCode, "NY"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeCNAME, endpoint.TTL(recordTTL), "foo.example.com").WithSetIdentifier("test-set-1").WithProviderSpecific(providerSpecificWeight, "10").WithProviderSpecific(providerSpecificHealthCheckID, "foo-bar-healthcheck-id"),
|
||||
endpoint.NewEndpointWithTTL("healthcheck-test.zone-1.ext-dns-test-2.teapot.zalan.do", endpoint.RecordTypeA, endpoint.TTL(recordTTL), "4.3.2.1").WithSetIdentifier("test-set-2").WithProviderSpecific(providerSpecificWeight, "20").WithProviderSpecific(providerSpecificHealthCheckID, "abc-def-healthcheck-id"),
|
||||
})
|
||||
|
@ -18,12 +18,10 @@ package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@ -446,59 +444,6 @@ func testAzureApplyChangesInternal(t *testing.T, dryRun bool, client RecordSetsC
|
||||
}
|
||||
}
|
||||
|
||||
func TestAzureGetAccessToken(t *testing.T) {
|
||||
env := azure.PublicCloud
|
||||
cfg := config{
|
||||
ClientID: "",
|
||||
ClientSecret: "",
|
||||
TenantID: "",
|
||||
UseManagedIdentityExtension: false,
|
||||
}
|
||||
|
||||
_, err := getAccessToken(cfg, env)
|
||||
if err == nil {
|
||||
t.Fatalf("expected to fail, but got no error")
|
||||
}
|
||||
|
||||
// Expect to use managed identity in this case
|
||||
cfg = config{
|
||||
ClientID: "msi",
|
||||
ClientSecret: "msi",
|
||||
TenantID: "cefe8aef-5127-4d65-a299-012053f81f60",
|
||||
UserAssignedIdentityID: "userAssignedIdentityClientID",
|
||||
UseManagedIdentityExtension: true,
|
||||
}
|
||||
token, err := getAccessToken(cfg, env)
|
||||
if err != nil {
|
||||
t.Fatalf("expected to construct a token successfully, but got error %v", err)
|
||||
}
|
||||
_, err = token.MarshalJSON()
|
||||
if err == nil ||
|
||||
!strings.Contains(err.Error(), "marshalling ServicePrincipalMSISecret is not supported") {
|
||||
t.Fatalf("expected to fail to marshal token, but got %v", err)
|
||||
}
|
||||
|
||||
// Expect to use SPN in this case
|
||||
cfg = config{
|
||||
ClientID: "SPNClientID",
|
||||
ClientSecret: "SPNSecret",
|
||||
TenantID: "cefe8aef-5127-4d65-a299-012053f81f60",
|
||||
UserAssignedIdentityID: "userAssignedIdentityClientID",
|
||||
UseManagedIdentityExtension: true,
|
||||
}
|
||||
token, err = getAccessToken(cfg, env)
|
||||
if err != nil {
|
||||
t.Fatalf("expected to construct a token successfully, but got error %v", err)
|
||||
}
|
||||
innerToken, err := token.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("expected to marshal token successfully, but got error %v", err)
|
||||
}
|
||||
if !strings.Contains(string(innerToken), "SPNClientID") {
|
||||
t.Fatalf("expect the clientID of the token is SPNClientID, but got token %s", string(innerToken))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAzureNameFilter(t *testing.T) {
|
||||
provider, err := newMockedAzureProvider(endpoint.NewDomainFilter([]string{"nginx.example.com"}), endpoint.NewDomainFilter([]string{"example.com"}), provider.NewZoneIDFilter([]string{""}), true, "k8s", "",
|
||||
&[]dns.Zone{
|
||||
|
@ -19,7 +19,6 @@ package azure
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
@ -104,10 +103,6 @@ func getAccessToken(cfg config, environment azure.Environment) (*adal.ServicePri
|
||||
// Try to retrieve token with MSI.
|
||||
if cfg.UseManagedIdentityExtension {
|
||||
log.Info("Using managed identity extension to retrieve access token for Azure API.")
|
||||
os.Setenv("MSI_ENDPOINT", "http://dummy")
|
||||
defer func() {
|
||||
os.Unsetenv("MSI_ENDPOINT")
|
||||
}()
|
||||
|
||||
if cfg.UserAssignedIdentityID != "" {
|
||||
log.Infof("Resolving to user assigned identity, client id is %s.", cfg.UserAssignedIdentityID)
|
||||
|
@ -587,10 +587,7 @@ func getBluecatGatewayToken(cfg bluecatConfig) (string, http.Cookie, error) {
|
||||
return "", http.Cookie{}, errors.Wrap(err, "could not unmarshal credentials for bluecat gateway config")
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.SkipTLSVerify},
|
||||
}}
|
||||
c := newHTTPClient(cfg.SkipTLSVerify)
|
||||
|
||||
resp, err := c.Post(cfg.GatewayHost+"/rest_login", "application/json", bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
@ -622,12 +619,8 @@ func getBluecatGatewayToken(cfg bluecatConfig) (string, http.Cookie, error) {
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getBluecatZones(zoneName string) ([]BluecatZone, error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zoneName)
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath
|
||||
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||
@ -660,12 +653,7 @@ func (c GatewayClientConfig) getBluecatZones(zoneName string) ([]BluecatZone, er
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getHostRecords(zone string, records *[]BluecatHostRecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
|
||||
@ -692,12 +680,7 @@ func (c GatewayClientConfig) getHostRecords(zone string, records *[]BluecatHostR
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getCNAMERecords(zone string, records *[]BluecatCNAMERecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
|
||||
@ -724,12 +707,7 @@ func (c GatewayClientConfig) getCNAMERecords(zone string, records *[]BluecatCNAM
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getTXTRecords(zone string, records *[]BluecatTXTRecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
|
||||
@ -757,12 +735,7 @@ func (c GatewayClientConfig) getTXTRecords(zone string, records *[]BluecatTXTRec
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getHostRecord(name string, record *BluecatHostRecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -785,12 +758,7 @@ func (c GatewayClientConfig) getHostRecord(name string, record *BluecatHostRecor
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getCNAMERecord(name string, record *BluecatCNAMERecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -813,12 +781,7 @@ func (c GatewayClientConfig) getCNAMERecord(name string, record *BluecatCNAMERec
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) getTXTRecord(name string, record *BluecatTXTRecord) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -842,12 +805,7 @@ func (c GatewayClientConfig) getTXTRecord(name string, record *BluecatTXTRecord)
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) createHostRecord(zone string, req *bluecatCreateHostRecordRequest) (res interface{}, err error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
// Remove the trailing 'zones/'
|
||||
@ -866,12 +824,7 @@ func (c GatewayClientConfig) createHostRecord(zone string, req *bluecatCreateHos
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) createCNAMERecord(zone string, req *bluecatCreateCNAMERecordRequest) (res interface{}, err error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
// Remove the trailing 'zones/'
|
||||
@ -892,12 +845,7 @@ func (c GatewayClientConfig) createCNAMERecord(zone string, req *bluecatCreateCN
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (interface{}, error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
zonePath := expandZone(zone)
|
||||
// Remove the trailing 'zones/'
|
||||
@ -917,12 +865,7 @@ func (c GatewayClientConfig) createTXTRecord(zone string, req *bluecatCreateTXTR
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) deleteHostRecord(name string, zone string) (err error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -941,12 +884,7 @@ func (c GatewayClientConfig) deleteHostRecord(name string, zone string) (err err
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) deleteCNAMERecord(name string, zone string) (err error) {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -965,12 +903,7 @@ func (c GatewayClientConfig) deleteCNAMERecord(name string, zone string) (err er
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) deleteTXTRecord(name string, zone string) error {
|
||||
transportCfg := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: c.SkipTLSVerify},
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: transportCfg,
|
||||
}
|
||||
client := newHTTPClient(c.SkipTLSVerify)
|
||||
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||
"/views/" + c.View + "/" +
|
||||
@ -1042,3 +975,15 @@ func extractOwnerfromTXTRecord(propString string) (string, error) {
|
||||
}
|
||||
return strings.Split(match[0], "=")[1], nil
|
||||
}
|
||||
|
||||
// newHTTPClient returns an instance of http client
|
||||
func newHTTPClient(skipTLSVerify bool) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: skipTLSVerify,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,15 @@ const (
|
||||
defaultCloudFlareRecordTTL = 1
|
||||
)
|
||||
|
||||
// We have to use pointers to bools now, as the upstream cloudflare-go library requires them
|
||||
// see: https://github.com/cloudflare/cloudflare-go/pull/595
|
||||
|
||||
// proxyEnabled is a pointer to a bool true showing the record should be proxied through cloudflare
|
||||
var proxyEnabled *bool = boolPtr(true)
|
||||
|
||||
// proxyDisabled is a pointer to a bool false showing the record should not be proxied through cloudflare
|
||||
var proxyDisabled *bool = boolPtr(false)
|
||||
|
||||
var cloudFlareTypeNotSupported = map[string]bool{
|
||||
"LOC": true,
|
||||
"MX": true,
|
||||
@ -54,53 +63,53 @@ var cloudFlareTypeNotSupported = map[string]bool{
|
||||
|
||||
// cloudFlareDNS is the subset of the CloudFlare API that we actually use. Add methods as required. Signatures must match exactly.
|
||||
type cloudFlareDNS interface {
|
||||
UserDetails() (cloudflare.User, error)
|
||||
UserDetails(ctx context.Context) (cloudflare.User, error)
|
||||
ZoneIDByName(zoneName string) (string, error)
|
||||
ListZones(zoneID ...string) ([]cloudflare.Zone, error)
|
||||
ListZones(ctx context.Context, zoneID ...string) ([]cloudflare.Zone, error)
|
||||
ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error)
|
||||
ZoneDetails(zoneID string) (cloudflare.Zone, error)
|
||||
DNSRecords(zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error)
|
||||
CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error)
|
||||
DeleteDNSRecord(zoneID, recordID string) error
|
||||
UpdateDNSRecord(zoneID, recordID string, rr cloudflare.DNSRecord) error
|
||||
ZoneDetails(ctx context.Context, zoneID string) (cloudflare.Zone, error)
|
||||
DNSRecords(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error)
|
||||
CreateDNSRecord(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error)
|
||||
DeleteDNSRecord(ctx context.Context, zoneID, recordID string) error
|
||||
UpdateDNSRecord(ctx context.Context, zoneID, recordID string, rr cloudflare.DNSRecord) error
|
||||
}
|
||||
|
||||
type zoneService struct {
|
||||
service *cloudflare.API
|
||||
}
|
||||
|
||||
func (z zoneService) UserDetails() (cloudflare.User, error) {
|
||||
return z.service.UserDetails()
|
||||
func (z zoneService) UserDetails(ctx context.Context) (cloudflare.User, error) {
|
||||
return z.service.UserDetails(ctx)
|
||||
}
|
||||
|
||||
func (z zoneService) ListZones(zoneID ...string) ([]cloudflare.Zone, error) {
|
||||
return z.service.ListZones(zoneID...)
|
||||
func (z zoneService) ListZones(ctx context.Context, zoneID ...string) ([]cloudflare.Zone, error) {
|
||||
return z.service.ListZones(ctx, zoneID...)
|
||||
}
|
||||
|
||||
func (z zoneService) ZoneIDByName(zoneName string) (string, error) {
|
||||
return z.service.ZoneIDByName(zoneName)
|
||||
}
|
||||
|
||||
func (z zoneService) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) {
|
||||
return z.service.CreateDNSRecord(zoneID, rr)
|
||||
func (z zoneService) CreateDNSRecord(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) {
|
||||
return z.service.CreateDNSRecord(ctx, zoneID, rr)
|
||||
}
|
||||
|
||||
func (z zoneService) DNSRecords(zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) {
|
||||
return z.service.DNSRecords(zoneID, rr)
|
||||
func (z zoneService) DNSRecords(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) {
|
||||
return z.service.DNSRecords(ctx, zoneID, rr)
|
||||
}
|
||||
func (z zoneService) UpdateDNSRecord(zoneID, recordID string, rr cloudflare.DNSRecord) error {
|
||||
return z.service.UpdateDNSRecord(zoneID, recordID, rr)
|
||||
func (z zoneService) UpdateDNSRecord(ctx context.Context, zoneID, recordID string, rr cloudflare.DNSRecord) error {
|
||||
return z.service.UpdateDNSRecord(ctx, zoneID, recordID, rr)
|
||||
}
|
||||
func (z zoneService) DeleteDNSRecord(zoneID, recordID string) error {
|
||||
return z.service.DeleteDNSRecord(zoneID, recordID)
|
||||
func (z zoneService) DeleteDNSRecord(ctx context.Context, zoneID, recordID string) error {
|
||||
return z.service.DeleteDNSRecord(ctx, zoneID, recordID)
|
||||
}
|
||||
|
||||
func (z zoneService) ListZonesContext(ctx context.Context, opts ...cloudflare.ReqOption) (cloudflare.ZonesResponse, error) {
|
||||
return z.service.ListZonesContext(ctx, opts...)
|
||||
}
|
||||
|
||||
func (z zoneService) ZoneDetails(zoneID string) (cloudflare.Zone, error) {
|
||||
return z.service.ZoneDetails(zoneID)
|
||||
func (z zoneService) ZoneDetails(ctx context.Context, zoneID string) (cloudflare.Zone, error) {
|
||||
return z.service.ZoneDetails(ctx, zoneID)
|
||||
}
|
||||
|
||||
// CloudFlareProvider is an implementation of Provider for CloudFlare DNS.
|
||||
@ -162,7 +171,7 @@ func (p *CloudFlareProvider) Zones(ctx context.Context) ([]cloudflare.Zone, erro
|
||||
log.Debugln("zoneIDFilter configured. only looking up zone IDs defined")
|
||||
for _, zoneID := range p.zoneIDFilter.ZoneIDs {
|
||||
log.Debugf("looking up zone %s", zoneID)
|
||||
detailResponse, err := p.Client.ZoneDetails(zoneID)
|
||||
detailResponse, err := p.Client.ZoneDetails(ctx, zoneID)
|
||||
if err != nil {
|
||||
log.Errorf("zone %s lookup failed, %v", zoneID, err)
|
||||
continue
|
||||
@ -177,24 +186,20 @@ func (p *CloudFlareProvider) Zones(ctx context.Context) ([]cloudflare.Zone, erro
|
||||
}
|
||||
|
||||
log.Debugln("no zoneIDFilter configured, looking at all zones")
|
||||
for {
|
||||
zonesResponse, err := p.Client.ListZonesContext(ctx, cloudflare.WithPagination(p.PaginationOptions))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, zone := range zonesResponse.Result {
|
||||
if !p.domainFilter.Match(zone.Name) {
|
||||
log.Debugf("zone %s not in domain filter", zone.Name)
|
||||
continue
|
||||
}
|
||||
result = append(result, zone)
|
||||
}
|
||||
if p.PaginationOptions.Page == zonesResponse.ResultInfo.TotalPages {
|
||||
break
|
||||
}
|
||||
p.PaginationOptions.Page++
|
||||
zonesResponse, err := p.Client.ListZonesContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, zone := range zonesResponse.Result {
|
||||
if !p.domainFilter.Match(zone.Name) {
|
||||
log.Debugf("zone %s not in domain filter", zone.Name)
|
||||
continue
|
||||
}
|
||||
result = append(result, zone)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -207,7 +212,7 @@ func (p *CloudFlareProvider) Records(ctx context.Context) ([]*endpoint.Endpoint,
|
||||
|
||||
endpoints := []*endpoint.Endpoint{}
|
||||
for _, zone := range zones {
|
||||
records, err := p.Client.DNSRecords(zone.ID, cloudflare.DNSRecord{})
|
||||
records, err := p.Client.DNSRecords(ctx, zone.ID, cloudflare.DNSRecord{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -281,7 +286,7 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
|
||||
changesByZone := p.changesByZone(zones, changes)
|
||||
|
||||
for zoneID, changes := range changesByZone {
|
||||
records, err := p.Client.DNSRecords(zoneID, cloudflare.DNSRecord{})
|
||||
records, err := p.Client.DNSRecords(ctx, zoneID, cloudflare.DNSRecord{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not fetch records from zone, %v", err)
|
||||
}
|
||||
@ -306,7 +311,7 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
|
||||
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.ResourceRecord)
|
||||
continue
|
||||
}
|
||||
err := p.Client.UpdateDNSRecord(zoneID, recordID, change.ResourceRecord)
|
||||
err := p.Client.UpdateDNSRecord(ctx, zoneID, recordID, change.ResourceRecord)
|
||||
if err != nil {
|
||||
log.WithFields(logFields).Errorf("failed to update record: %v", err)
|
||||
}
|
||||
@ -316,12 +321,12 @@ func (p *CloudFlareProvider) submitChanges(ctx context.Context, changes []*cloud
|
||||
log.WithFields(logFields).Errorf("failed to find previous record: %v", change.ResourceRecord)
|
||||
continue
|
||||
}
|
||||
err := p.Client.DeleteDNSRecord(zoneID, recordID)
|
||||
err := p.Client.DeleteDNSRecord(ctx, zoneID, recordID)
|
||||
if err != nil {
|
||||
log.WithFields(logFields).Errorf("failed to delete record: %v", err)
|
||||
}
|
||||
} else if change.Action == cloudFlareCreate {
|
||||
_, err := p.Client.CreateDNSRecord(zoneID, change.ResourceRecord)
|
||||
_, err := p.Client.CreateDNSRecord(ctx, zoneID, change.ResourceRecord)
|
||||
if err != nil {
|
||||
log.WithFields(logFields).Errorf("failed to create record: %v", err)
|
||||
}
|
||||
@ -382,16 +387,12 @@ func (p *CloudFlareProvider) newCloudFlareChange(action string, endpoint *endpoi
|
||||
ttl = int(endpoint.RecordTTL)
|
||||
}
|
||||
|
||||
if len(endpoint.Targets) > 1 {
|
||||
log.Errorf("Updates should have just one target")
|
||||
}
|
||||
|
||||
return &cloudFlareChange{
|
||||
Action: action,
|
||||
ResourceRecord: cloudflare.DNSRecord{
|
||||
Name: endpoint.DNSName,
|
||||
TTL: ttl,
|
||||
Proxied: proxied,
|
||||
Proxied: &proxied,
|
||||
Type: endpoint.RecordType,
|
||||
Content: target,
|
||||
},
|
||||
@ -450,8 +451,15 @@ func groupByNameAndType(records []cloudflare.DNSRecord) []*endpoint.Endpoint {
|
||||
records[0].Type,
|
||||
endpoint.TTL(records[0].TTL),
|
||||
targets...).
|
||||
WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(records[0].Proxied)))
|
||||
WithProviderSpecific(source.CloudflareProxiedKey, strconv.FormatBool(*records[0].Proxied)),
|
||||
)
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
// boolPtr is used as a helper function to return a pointer to a boolean
|
||||
// Needed because some parameters require a pointer.
|
||||
func boolPtr(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ var ExampleDomain = []cloudflare.DNSRecord{
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 120,
|
||||
Content: "1.2.3.4",
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
ID: "2345678901",
|
||||
@ -64,7 +64,7 @@ var ExampleDomain = []cloudflare.DNSRecord{
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 120,
|
||||
Content: "3.4.5.6",
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
ID: "1231231233",
|
||||
@ -73,7 +73,7 @@ var ExampleDomain = []cloudflare.DNSRecord{
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 1,
|
||||
Content: "2.3.4.5",
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ func NewMockCloudFlareClientWithRecords(records map[string][]cloudflare.DNSRecor
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) CreateDNSRecord(zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) {
|
||||
func (m *mockCloudFlareClient) CreateDNSRecord(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) {
|
||||
m.Actions = append(m.Actions, MockAction{
|
||||
Name: "Create",
|
||||
ZoneId: zoneID,
|
||||
@ -118,7 +118,7 @@ func (m *mockCloudFlareClient) CreateDNSRecord(zoneID string, rr cloudflare.DNSR
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) DNSRecords(zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) {
|
||||
func (m *mockCloudFlareClient) DNSRecords(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) {
|
||||
if m.dnsRecordsError != nil {
|
||||
return nil, m.dnsRecordsError
|
||||
}
|
||||
@ -132,7 +132,7 @@ func (m *mockCloudFlareClient) DNSRecords(zoneID string, rr cloudflare.DNSRecord
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) UpdateDNSRecord(zoneID, recordID string, rr cloudflare.DNSRecord) error {
|
||||
func (m *mockCloudFlareClient) UpdateDNSRecord(ctx context.Context, zoneID, recordID string, rr cloudflare.DNSRecord) error {
|
||||
m.Actions = append(m.Actions, MockAction{
|
||||
Name: "Update",
|
||||
ZoneId: zoneID,
|
||||
@ -147,7 +147,7 @@ func (m *mockCloudFlareClient) UpdateDNSRecord(zoneID, recordID string, rr cloud
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) DeleteDNSRecord(zoneID, recordID string) error {
|
||||
func (m *mockCloudFlareClient) DeleteDNSRecord(ctx context.Context, zoneID, recordID string) error {
|
||||
m.Actions = append(m.Actions, MockAction{
|
||||
Name: "Delete",
|
||||
ZoneId: zoneID,
|
||||
@ -162,7 +162,7 @@ func (m *mockCloudFlareClient) DeleteDNSRecord(zoneID, recordID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) UserDetails() (cloudflare.User, error) {
|
||||
func (m *mockCloudFlareClient) UserDetails(ctx context.Context) (cloudflare.User, error) {
|
||||
return m.User, nil
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ func (m *mockCloudFlareClient) ZoneIDByName(zoneName string) (string, error) {
|
||||
return "", errors.New("Unknown zone: " + zoneName)
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) ListZones(zoneID ...string) ([]cloudflare.Zone, error) {
|
||||
func (m *mockCloudFlareClient) ListZones(ctx context.Context, zoneID ...string) ([]cloudflare.Zone, error) {
|
||||
if m.listZonesError != nil {
|
||||
return nil, m.listZonesError
|
||||
}
|
||||
@ -216,7 +216,7 @@ func (m *mockCloudFlareClient) ListZonesContext(ctx context.Context, opts ...clo
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *mockCloudFlareClient) ZoneDetails(zoneID string) (cloudflare.Zone, error) {
|
||||
func (m *mockCloudFlareClient) ZoneDetails(ctx context.Context, zoneID string) (cloudflare.Zone, error) {
|
||||
for id, zoneName := range m.Zones {
|
||||
if zoneID == id {
|
||||
return cloudflare.Zone{
|
||||
@ -292,7 +292,7 @@ func TestCloudflareA(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 1,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -303,7 +303,7 @@ func TestCloudflareA(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.2",
|
||||
TTL: 1,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -330,7 +330,7 @@ func TestCloudflareCname(t *testing.T) {
|
||||
Name: "cname.bar.com",
|
||||
Content: "google.com",
|
||||
TTL: 1,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -341,7 +341,7 @@ func TestCloudflareCname(t *testing.T) {
|
||||
Name: "cname.bar.com",
|
||||
Content: "facebook.com",
|
||||
TTL: 1,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -368,7 +368,7 @@ func TestCloudflareCustomTTL(t *testing.T) {
|
||||
Name: "ttl.bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 120,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -394,7 +394,7 @@ func TestCloudflareProxiedDefault(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 1,
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -426,7 +426,7 @@ func TestCloudflareProxiedOverrideTrue(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 1,
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -458,7 +458,7 @@ func TestCloudflareProxiedOverrideFalse(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 1,
|
||||
Proxied: false,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -490,7 +490,7 @@ func TestCloudflareProxiedOverrideIllegal(t *testing.T) {
|
||||
Name: "bar.com",
|
||||
Content: "127.0.0.1",
|
||||
TTL: 1,
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -499,19 +499,21 @@ func TestCloudflareProxiedOverrideIllegal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCloudflareSetProxied(t *testing.T) {
|
||||
var proxied *bool = proxyEnabled
|
||||
var notProxied *bool = proxyDisabled
|
||||
var testCases = []struct {
|
||||
recordType string
|
||||
domain string
|
||||
proxiable bool
|
||||
proxiable *bool
|
||||
}{
|
||||
{"A", "bar.com", true},
|
||||
{"CNAME", "bar.com", true},
|
||||
{"TXT", "bar.com", false},
|
||||
{"MX", "bar.com", false},
|
||||
{"NS", "bar.com", false},
|
||||
{"SPF", "bar.com", false},
|
||||
{"SRV", "bar.com", false},
|
||||
{"A", "*.bar.com", false},
|
||||
{"A", "bar.com", proxied},
|
||||
{"CNAME", "bar.com", proxied},
|
||||
{"TXT", "bar.com", notProxied},
|
||||
{"MX", "bar.com", notProxied},
|
||||
{"NS", "bar.com", notProxied},
|
||||
{"SPF", "bar.com", notProxied},
|
||||
{"SRV", "bar.com", notProxied},
|
||||
{"A", "*.bar.com", notProxied},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
@ -684,6 +686,7 @@ func TestCloudflareApplyChanges(t *testing.T) {
|
||||
Name: "new.bar.com",
|
||||
Content: "target",
|
||||
TTL: 1,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -693,6 +696,7 @@ func TestCloudflareApplyChanges(t *testing.T) {
|
||||
Name: "foobar.bar.com",
|
||||
Content: "target-new",
|
||||
TTL: 1,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -779,6 +783,7 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
ExpectedEndpoints: []*endpoint.Endpoint{
|
||||
@ -805,12 +810,14 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "foo.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.2",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
ExpectedEndpoints: []*endpoint.Endpoint{
|
||||
@ -837,24 +844,28 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "foo.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.2",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "bar.de",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "bar.de",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.2",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
ExpectedEndpoints: []*endpoint.Endpoint{
|
||||
@ -894,18 +905,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "foo.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.2",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "bar.de",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
ExpectedEndpoints: []*endpoint.Endpoint{
|
||||
@ -945,18 +959,21 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "foo.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
Content: "10.10.10.2",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
{
|
||||
Name: "bar.de",
|
||||
Type: "NOT SUPPORTED",
|
||||
Content: "10.10.10.1",
|
||||
TTL: defaultCloudFlareRecordTTL,
|
||||
Proxied: proxyDisabled,
|
||||
},
|
||||
},
|
||||
ExpectedEndpoints: []*endpoint.Endpoint{
|
||||
@ -984,94 +1001,101 @@ func TestCloudflareGroupByNameAndType(t *testing.T) {
|
||||
|
||||
func TestProviderPropertiesIdempotency(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
ProviderProxiedByDefault bool
|
||||
RecordsAreProxied bool
|
||||
RecordsAreProxied *bool
|
||||
ShouldBeUpdated bool
|
||||
}{
|
||||
{
|
||||
Name: "ProxyDefault: false, ShouldBeProxied: false, ExpectUpdates: false",
|
||||
ProviderProxiedByDefault: false,
|
||||
RecordsAreProxied: false,
|
||||
RecordsAreProxied: proxyDisabled,
|
||||
ShouldBeUpdated: false,
|
||||
},
|
||||
{
|
||||
Name: "ProxyDefault: true, ShouldBeProxied: true, ExpectUpdates: false",
|
||||
ProviderProxiedByDefault: true,
|
||||
RecordsAreProxied: true,
|
||||
RecordsAreProxied: proxyEnabled,
|
||||
ShouldBeUpdated: false,
|
||||
},
|
||||
{
|
||||
Name: "ProxyDefault: true, ShouldBeProxied: false, ExpectUpdates: true",
|
||||
ProviderProxiedByDefault: true,
|
||||
RecordsAreProxied: false,
|
||||
RecordsAreProxied: proxyDisabled,
|
||||
ShouldBeUpdated: true,
|
||||
},
|
||||
{
|
||||
Name: "ProxyDefault: false, ShouldBeProxied: true, ExpectUpdates: true",
|
||||
ProviderProxiedByDefault: false,
|
||||
RecordsAreProxied: true,
|
||||
RecordsAreProxied: proxyEnabled,
|
||||
ShouldBeUpdated: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
client := NewMockCloudFlareClientWithRecords(map[string][]cloudflare.DNSRecord{
|
||||
"001": {
|
||||
{
|
||||
ID: "1234567890",
|
||||
ZoneID: "001",
|
||||
Name: "foobar.bar.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 120,
|
||||
Content: "1.2.3.4",
|
||||
Proxied: test.RecordsAreProxied,
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
client := NewMockCloudFlareClientWithRecords(map[string][]cloudflare.DNSRecord{
|
||||
"001": {
|
||||
{
|
||||
ID: "1234567890",
|
||||
ZoneID: "001",
|
||||
Name: "foobar.bar.com",
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 120,
|
||||
Content: "1.2.3.4",
|
||||
Proxied: test.RecordsAreProxied,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
provider := &CloudFlareProvider{
|
||||
Client: client,
|
||||
proxiedByDefault: test.ProviderProxiedByDefault,
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
current, err := provider.Records(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
assert.Equal(t, 1, len(current))
|
||||
|
||||
desired := []*endpoint.Endpoint{}
|
||||
for _, c := range current {
|
||||
// Copy all except ProviderSpecific fields
|
||||
desired = append(desired, &endpoint.Endpoint{
|
||||
DNSName: c.DNSName,
|
||||
Targets: c.Targets,
|
||||
RecordType: c.RecordType,
|
||||
SetIdentifier: c.SetIdentifier,
|
||||
RecordTTL: c.RecordTTL,
|
||||
Labels: c.Labels,
|
||||
})
|
||||
}
|
||||
|
||||
plan := plan.Plan{
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
PropertyComparator: provider.PropertyValuesEqual,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
provider := &CloudFlareProvider{
|
||||
Client: client,
|
||||
proxiedByDefault: test.ProviderProxiedByDefault,
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
plan = *plan.Calculate()
|
||||
assert.NotNil(t, plan.Changes, "should have plan")
|
||||
if plan.Changes == nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 0, len(plan.Changes.Create), "should not have creates")
|
||||
assert.Equal(t, 0, len(plan.Changes.Delete), "should not have deletes")
|
||||
current, err := provider.Records(ctx)
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
assert.Equal(t, 1, len(current))
|
||||
|
||||
if test.ShouldBeUpdated {
|
||||
assert.Equal(t, 1, len(plan.Changes.UpdateNew), "should not have new updates")
|
||||
assert.Equal(t, 1, len(plan.Changes.UpdateOld), "should not have old updates")
|
||||
} else {
|
||||
assert.Equal(t, 0, len(plan.Changes.UpdateNew), "should not have new updates")
|
||||
assert.Equal(t, 0, len(plan.Changes.UpdateOld), "should not have old updates")
|
||||
}
|
||||
desired := []*endpoint.Endpoint{}
|
||||
for _, c := range current {
|
||||
// Copy all except ProviderSpecific fields
|
||||
desired = append(desired, &endpoint.Endpoint{
|
||||
DNSName: c.DNSName,
|
||||
Targets: c.Targets,
|
||||
RecordType: c.RecordType,
|
||||
SetIdentifier: c.SetIdentifier,
|
||||
RecordTTL: c.RecordTTL,
|
||||
Labels: c.Labels,
|
||||
})
|
||||
}
|
||||
|
||||
plan := plan.Plan{
|
||||
Current: current,
|
||||
Desired: desired,
|
||||
PropertyComparator: provider.PropertyValuesEqual,
|
||||
ManagedRecords: []string{endpoint.RecordTypeA, endpoint.RecordTypeCNAME},
|
||||
}
|
||||
|
||||
plan = *plan.Calculate()
|
||||
assert.NotNil(t, plan.Changes, "should have plan")
|
||||
if plan.Changes == nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 0, len(plan.Changes.Create), "should not have creates")
|
||||
assert.Equal(t, 0, len(plan.Changes.Delete), "should not have deletes")
|
||||
|
||||
if test.ShouldBeUpdated {
|
||||
assert.Equal(t, 1, len(plan.Changes.UpdateNew), "should not have new updates")
|
||||
assert.Equal(t, 1, len(plan.Changes.UpdateOld), "should not have old updates")
|
||||
} else {
|
||||
assert.Equal(t, 0, len(plan.Changes.UpdateNew), "should not have new updates")
|
||||
assert.Equal(t, 0, len(plan.Changes.UpdateOld), "should not have old updates")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1129,7 +1153,7 @@ func TestCloudflareComplexUpdate(t *testing.T) {
|
||||
Type: "A",
|
||||
Content: "2.3.4.5",
|
||||
TTL: 1,
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
MockAction{
|
||||
@ -1141,7 +1165,7 @@ func TestCloudflareComplexUpdate(t *testing.T) {
|
||||
Type: "A",
|
||||
Content: "1.2.3.4",
|
||||
TTL: 1,
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
MockAction{
|
||||
@ -1162,7 +1186,7 @@ func TestCustomTTLWithEnabledProxyNotChanged(t *testing.T) {
|
||||
Type: endpoint.RecordTypeA,
|
||||
TTL: 1,
|
||||
Content: "1.2.3.4",
|
||||
Proxied: true,
|
||||
Proxied: proxyEnabled,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -176,6 +176,9 @@ OuterLoop:
|
||||
case dns.TypeTXT:
|
||||
rrValues = (rr.(*dns.TXT).Txt)
|
||||
rrType = "TXT"
|
||||
case dns.TypeNS:
|
||||
rrValues = []string{rr.(*dns.NS).Ns}
|
||||
rrType = "NS"
|
||||
default:
|
||||
continue // Unhandled record type
|
||||
}
|
||||
|
@ -172,6 +172,11 @@ func TestRfc2136ApplyChanges(t *testing.T) {
|
||||
RecordType: "TXT",
|
||||
Targets: []string{"boom"},
|
||||
},
|
||||
{
|
||||
DNSName: "ns.foobar.com",
|
||||
RecordType: "NS",
|
||||
Targets: []string{"boom"},
|
||||
},
|
||||
},
|
||||
Delete: []*endpoint.Endpoint{
|
||||
{
|
||||
@ -190,13 +195,16 @@ func TestRfc2136ApplyChanges(t *testing.T) {
|
||||
err = provider.ApplyChanges(context.Background(), p)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(stub.createMsgs))
|
||||
assert.Equal(t, 3, len(stub.createMsgs))
|
||||
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "v1.foo.com"))
|
||||
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "1.2.3.4"))
|
||||
|
||||
assert.True(t, strings.Contains(stub.createMsgs[1].String(), "v1.foobar.com"))
|
||||
assert.True(t, strings.Contains(stub.createMsgs[1].String(), "boom"))
|
||||
|
||||
assert.True(t, strings.Contains(stub.createMsgs[2].String(), "ns.foobar.com"))
|
||||
assert.True(t, strings.Contains(stub.createMsgs[2].String(), "boom"))
|
||||
|
||||
assert.Equal(t, 2, len(stub.updateMsgs))
|
||||
assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v2.foo.com"))
|
||||
assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com"))
|
||||
|
@ -1,3 +1,13 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
trivy image --exit-code 1 us.gcr.io/k8s-artifacts-prod/external-dns/external-dns:$(git describe --tags --always --dirty)
|
||||
# install trivy
|
||||
cd /tmp
|
||||
curl -LO https://github.com/aquasecurity/trivy/releases/download/v0.20.2/trivy_0.20.2_Linux-64bit.tar.gz
|
||||
echo "38a6de48e21a34e0fa0d2cf63439c0afcbbae0e78fb3feada7a84a9cf6e7f60c trivy_0.20.2_Linux-64bit.tar.gz" | sha256sum -c
|
||||
tar -xvf trivy_0.20.2_Linux-64bit.tar.gz
|
||||
chmod +x trivy
|
||||
|
||||
# run trivy
|
||||
cd -
|
||||
/tmp/trivy image --exit-code 1 us.gcr.io/k8s-artifacts-prod/external-dns/external-dns:$(git describe --tags --always --dirty)
|
||||
|
@ -49,6 +49,7 @@ type ocpRouteSource struct {
|
||||
ignoreHostnameAnnotation bool
|
||||
routeInformer routeInformer.RouteInformer
|
||||
labelSelector labels.Selector
|
||||
ocpRouterName string
|
||||
}
|
||||
|
||||
// NewOcpRouteSource creates a new ocpRouteSource with the given config.
|
||||
@ -60,6 +61,7 @@ func NewOcpRouteSource(
|
||||
combineFQDNAnnotation bool,
|
||||
ignoreHostnameAnnotation bool,
|
||||
labelSelector labels.Selector,
|
||||
ocpRouterName string,
|
||||
) (Source, error) {
|
||||
tmpl, err := parseTemplate(fqdnTemplate)
|
||||
if err != nil {
|
||||
@ -96,11 +98,16 @@ func NewOcpRouteSource(
|
||||
ignoreHostnameAnnotation: ignoreHostnameAnnotation,
|
||||
routeInformer: informer,
|
||||
labelSelector: labelSelector,
|
||||
ocpRouterName: ocpRouterName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO add a meaningful EventHandler
|
||||
func (ors *ocpRouteSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||
log.Debug("Adding event handler for openshift route")
|
||||
|
||||
// Right now there is no way to remove event handler from informer, see:
|
||||
// https://github.com/kubernetes/kubernetes/issues/79610
|
||||
ors.routeInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
|
||||
}
|
||||
|
||||
// Endpoints returns endpoint objects for each host-target combination that should be processed.
|
||||
@ -128,7 +135,7 @@ func (ors *ocpRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint,
|
||||
continue
|
||||
}
|
||||
|
||||
orEndpoints := endpointsFromOcpRoute(ocpRoute, ors.ignoreHostnameAnnotation)
|
||||
orEndpoints := ors.endpointsFromOcpRoute(ocpRoute, ors.ignoreHostnameAnnotation)
|
||||
|
||||
// apply template if host is missing on OpenShift Route
|
||||
if (ors.combineFQDNAnnotation || len(orEndpoints) == 0) && ors.fqdnTemplate != nil {
|
||||
@ -174,7 +181,7 @@ func (ors *ocpRouteSource) endpointsFromTemplate(ocpRoute *routev1.Route) ([]*en
|
||||
|
||||
targets := getTargetsFromTargetAnnotation(ocpRoute.Annotations)
|
||||
if len(targets) == 0 {
|
||||
targets = targetsFromOcpRouteStatus(ocpRoute.Status)
|
||||
targets = ors.targetsFromOcpRouteStatus(ocpRoute.Status)
|
||||
}
|
||||
|
||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ocpRoute.Annotations)
|
||||
@ -223,7 +230,7 @@ func (ors *ocpRouteSource) setResourceLabel(ocpRoute *routev1.Route, endpoints [
|
||||
}
|
||||
|
||||
// endpointsFromOcpRoute extracts the endpoints from a OpenShift Route object
|
||||
func endpointsFromOcpRoute(ocpRoute *routev1.Route, ignoreHostnameAnnotation bool) []*endpoint.Endpoint {
|
||||
func (ors *ocpRouteSource) endpointsFromOcpRoute(ocpRoute *routev1.Route, ignoreHostnameAnnotation bool) []*endpoint.Endpoint {
|
||||
var endpoints []*endpoint.Endpoint
|
||||
|
||||
ttl, err := getTTLFromAnnotations(ocpRoute.Annotations)
|
||||
@ -234,7 +241,7 @@ func endpointsFromOcpRoute(ocpRoute *routev1.Route, ignoreHostnameAnnotation boo
|
||||
targets := getTargetsFromTargetAnnotation(ocpRoute.Annotations)
|
||||
|
||||
if len(targets) == 0 {
|
||||
targets = targetsFromOcpRouteStatus(ocpRoute.Status)
|
||||
targets = ors.targetsFromOcpRouteStatus(ocpRoute.Status)
|
||||
}
|
||||
|
||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(ocpRoute.Annotations)
|
||||
@ -253,14 +260,18 @@ func endpointsFromOcpRoute(ocpRoute *routev1.Route, ignoreHostnameAnnotation boo
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func targetsFromOcpRouteStatus(status routev1.RouteStatus) endpoint.Targets {
|
||||
func (ors *ocpRouteSource) targetsFromOcpRouteStatus(status routev1.RouteStatus) endpoint.Targets {
|
||||
var targets endpoint.Targets
|
||||
|
||||
for _, ing := range status.Ingress {
|
||||
if ing.RouterCanonicalHostname != "" {
|
||||
if len(ors.ocpRouterName) != 0 {
|
||||
if ing.RouterName == ors.ocpRouterName {
|
||||
targets = append(targets, ing.RouterCanonicalHostname)
|
||||
return targets
|
||||
}
|
||||
} else if ing.RouterCanonicalHostname != "" {
|
||||
targets = append(targets, ing.RouterCanonicalHostname)
|
||||
return targets
|
||||
}
|
||||
}
|
||||
|
||||
return targets
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ func (suite *OCPRouteSuite) SetupTest() {
|
||||
false,
|
||||
false,
|
||||
labels.Everything(),
|
||||
"",
|
||||
)
|
||||
|
||||
suite.routeWithTargets = &routev1.Route{
|
||||
@ -147,6 +148,7 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
labelSelector,
|
||||
"",
|
||||
)
|
||||
|
||||
if ti.expectError {
|
||||
@ -160,8 +162,6 @@ func testOcpRouteSourceNewOcpRouteSource(t *testing.T) {
|
||||
|
||||
// testOcpRouteSourceEndpoints tests that various OCP routes generate the correct endpoints.
|
||||
func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
title string
|
||||
targetNamespace string
|
||||
@ -172,6 +172,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
expected []*endpoint.Endpoint
|
||||
expectError bool
|
||||
labelFilter string
|
||||
ocpRouterName string
|
||||
}{
|
||||
{
|
||||
title: "route with basic hostname and route status target",
|
||||
@ -196,6 +197,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-domain.com",
|
||||
@ -206,6 +208,119 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
title: "route with basic hostname and route status target with one RouterCanonicalHostname and one ocpRouterNames defined",
|
||||
targetNamespace: "",
|
||||
annotationFilter: "",
|
||||
fqdnTemplate: "",
|
||||
ignoreHostnameAnnotation: false,
|
||||
ocpRoute: &routev1.Route{
|
||||
Spec: routev1.RouteSpec{
|
||||
Host: "my-domain.com",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "route-with-target",
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Status: routev1.RouteStatus{
|
||||
Ingress: []routev1.RouteIngress{
|
||||
{
|
||||
RouterName: "default",
|
||||
RouterCanonicalHostname: "router-default.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "default",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-domain.com",
|
||||
Targets: []string{
|
||||
"router-default.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
title: "route with basic hostname and route status target with one RouterCanonicalHostname and one ocpRouterNames defined and two router canonical names",
|
||||
targetNamespace: "",
|
||||
annotationFilter: "",
|
||||
fqdnTemplate: "",
|
||||
ignoreHostnameAnnotation: false,
|
||||
ocpRoute: &routev1.Route{
|
||||
Spec: routev1.RouteSpec{
|
||||
Host: "my-domain.com",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "route-with-target",
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Status: routev1.RouteStatus{
|
||||
Ingress: []routev1.RouteIngress{
|
||||
{
|
||||
RouterName: "default",
|
||||
RouterCanonicalHostname: "router-default.my-domain.com",
|
||||
},
|
||||
{
|
||||
RouterName: "test",
|
||||
RouterCanonicalHostname: "router-test.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "default",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-domain.com",
|
||||
Targets: []string{
|
||||
"router-default.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
title: "route with basic hostname and route status target with one RouterCanonicalHostname and one ocpRouterName defined and two router canonical names",
|
||||
targetNamespace: "",
|
||||
annotationFilter: "",
|
||||
fqdnTemplate: "",
|
||||
ignoreHostnameAnnotation: false,
|
||||
ocpRoute: &routev1.Route{
|
||||
Spec: routev1.RouteSpec{
|
||||
Host: "my-domain.com",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "route-with-target",
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
Status: routev1.RouteStatus{
|
||||
Ingress: []routev1.RouteIngress{
|
||||
{
|
||||
RouterName: "default",
|
||||
RouterCanonicalHostname: "router-default.my-domain.com",
|
||||
},
|
||||
{
|
||||
RouterName: "test",
|
||||
RouterCanonicalHostname: "router-test.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "default",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-domain.com",
|
||||
Targets: []string{
|
||||
"router-default.my-domain.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
title: "route with incorrect externalDNS controller annotation",
|
||||
targetNamespace: "",
|
||||
@ -221,8 +336,9 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{},
|
||||
expectError: false,
|
||||
ocpRouterName: "",
|
||||
expected: []*endpoint.Endpoint{},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
title: "route with basic hostname and annotation target",
|
||||
@ -242,6 +358,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-annotation-domain.com",
|
||||
@ -273,6 +390,7 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ocpRouterName: "",
|
||||
expected: []*endpoint.Endpoint{
|
||||
{
|
||||
DNSName: "my-annotation-domain.com",
|
||||
@ -304,17 +422,16 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*endpoint.Endpoint{},
|
||||
expectError: false,
|
||||
ocpRouterName: "",
|
||||
expected: []*endpoint.Endpoint{},
|
||||
expectError: false,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a Kubernetes testing client
|
||||
fakeClient := fake.NewSimpleClientset()
|
||||
|
||||
_, err := fakeClient.RouteV1().Routes(tc.ocpRoute.Namespace).Create(context.Background(), tc.ocpRoute, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -329,7 +446,9 @@ func testOcpRouteSourceEndpoints(t *testing.T) {
|
||||
false,
|
||||
false,
|
||||
labelSelector,
|
||||
tc.ocpRouterName,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := source.Endpoints(context.Background())
|
||||
|
@ -223,6 +223,7 @@ func (sc *serviceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, e
|
||||
lastMergedEndpoint := len(mergedEndpoints) - 1
|
||||
if mergedEndpoints[lastMergedEndpoint].DNSName == endpoints[i].DNSName &&
|
||||
mergedEndpoints[lastMergedEndpoint].RecordType == endpoints[i].RecordType &&
|
||||
mergedEndpoints[lastMergedEndpoint].SetIdentifier == endpoints[i].SetIdentifier &&
|
||||
mergedEndpoints[lastMergedEndpoint].RecordTTL == endpoints[i].RecordTTL {
|
||||
mergedEndpoints[lastMergedEndpoint].Targets = append(mergedEndpoints[lastMergedEndpoint].Targets, endpoints[i].Targets[0])
|
||||
} else {
|
||||
|
@ -1085,7 +1085,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
ignoreHostnameAnnotation bool
|
||||
labels map[string]string
|
||||
clusterIP string
|
||||
hostnames map[string]string
|
||||
services map[string]map[string]string
|
||||
serviceTypesFilter []string
|
||||
expected []*endpoint.Endpoint
|
||||
expectError bool
|
||||
@ -1103,8 +1103,8 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
false,
|
||||
map[string]string{},
|
||||
"",
|
||||
map[string]string{
|
||||
"1.2.3.4": "foo.example.org",
|
||||
map[string]map[string]string{
|
||||
"1.2.3.4": {hostnameAnnotationKey: "foo.example.org"},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
@ -1125,10 +1125,10 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
false,
|
||||
map[string]string{},
|
||||
"",
|
||||
map[string]string{
|
||||
"1.2.3.4": "foo.example.org",
|
||||
"1.2.3.5": "foo.example.org",
|
||||
"1.2.3.6": "foo.example.org",
|
||||
map[string]map[string]string{
|
||||
"1.2.3.4": {hostnameAnnotationKey: "foo.example.org"},
|
||||
"1.2.3.5": {hostnameAnnotationKey: "foo.example.org"},
|
||||
"1.2.3.6": {hostnameAnnotationKey: "foo.example.org"},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
@ -1149,14 +1149,14 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
false,
|
||||
map[string]string{},
|
||||
"",
|
||||
map[string]string{
|
||||
"1.2.3.5": "foo.example.org",
|
||||
"10.1.1.3": "bar.example.org",
|
||||
"10.1.1.1": "bar.example.org",
|
||||
"1.2.3.4": "foo.example.org",
|
||||
"10.1.1.2": "bar.example.org",
|
||||
"20.1.1.1": "foobar.example.org",
|
||||
"1.2.3.6": "foo.example.org",
|
||||
map[string]map[string]string{
|
||||
"1.2.3.5": {hostnameAnnotationKey: "foo.example.org"},
|
||||
"10.1.1.3": {hostnameAnnotationKey: "bar.example.org"},
|
||||
"10.1.1.1": {hostnameAnnotationKey: "bar.example.org"},
|
||||
"1.2.3.4": {hostnameAnnotationKey: "foo.example.org"},
|
||||
"10.1.1.2": {hostnameAnnotationKey: "bar.example.org"},
|
||||
"20.1.1.1": {hostnameAnnotationKey: "foobar.example.org"},
|
||||
"1.2.3.6": {hostnameAnnotationKey: "foo.example.org"},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
@ -1166,6 +1166,30 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"test that services with different set-identifier do not get merged together",
|
||||
"",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
v1.ServiceTypeLoadBalancer,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
map[string]string{},
|
||||
"",
|
||||
map[string]map[string]string{
|
||||
"a.elb.com": {hostnameAnnotationKey: "foo.example.org", SetIdentifierKey: "a"},
|
||||
"b.elb.com": {hostnameAnnotationKey: "foo.example.org", SetIdentifierKey: "b"},
|
||||
},
|
||||
[]string{},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Targets: endpoint.Targets{"a.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/fooa.elb.com"}, SetIdentifier: "a"},
|
||||
{DNSName: "foo.example.org", Targets: endpoint.Targets{"b.elb.com"}, Labels: map[string]string{endpoint.ResourceLabelKey: "service/testing/foob.elb.com"}, SetIdentifier: "b"},
|
||||
},
|
||||
false,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
@ -1175,12 +1199,9 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
kubernetes := fake.NewSimpleClientset()
|
||||
|
||||
// Create services to test against
|
||||
for serviceip, hostname := range tc.hostnames {
|
||||
for lb, annotations := range tc.services {
|
||||
ingresses := []v1.LoadBalancerIngress{}
|
||||
ingresses = append(ingresses, v1.LoadBalancerIngress{IP: serviceip})
|
||||
|
||||
annotations := make(map[string]string)
|
||||
annotations[hostnameAnnotationKey] = hostname
|
||||
ingresses = append(ingresses, v1.LoadBalancerIngress{IP: lb})
|
||||
|
||||
service := &v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
@ -1189,7 +1210,7 @@ func testMultipleServicesEndpoints(t *testing.T) {
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: tc.svcNamespace,
|
||||
Name: tc.svcName + serviceip,
|
||||
Name: tc.svcName + lb,
|
||||
Labels: tc.labels,
|
||||
Annotations: annotations,
|
||||
},
|
||||
|
@ -68,6 +68,7 @@ type Config struct {
|
||||
SkipperRouteGroupVersion string
|
||||
RequestTimeout time.Duration
|
||||
DefaultTargets []string
|
||||
OCPRouterName string
|
||||
}
|
||||
|
||||
// ClientGenerator provides clients
|
||||
@ -255,7 +256,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewOcpRouteSource(ocpClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter)
|
||||
return NewOcpRouteSource(ocpClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation, cfg.LabelFilter, cfg.OCPRouterName)
|
||||
case "fake":
|
||||
return NewFakeSource(cfg.FQDNTemplate)
|
||||
case "connector":
|
||||
|
Loading…
Reference in New Issue
Block a user