mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Merge branch 'master' into master
This commit is contained in:
commit
f9ad22cbbd
@ -27,7 +27,7 @@ COPY . .
|
|||||||
RUN make test build.$ARCH
|
RUN make test build.$ARCH
|
||||||
|
|
||||||
# final image
|
# final image
|
||||||
FROM $ARCH/alpine:3.12
|
FROM $ARCH/alpine:3.13
|
||||||
|
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
COPY --from=builder /sigs.k8s.io/external-dns/build/external-dns /bin/external-dns
|
||||||
|
24
Makefile
24
Makefile
@ -27,6 +27,23 @@ cover:
|
|||||||
cover-html: cover
|
cover-html: cover
|
||||||
go tool cover -html cover.out
|
go tool cover -html cover.out
|
||||||
|
|
||||||
|
# find or download controller-gen
|
||||||
|
# download controller-gen if necessary
|
||||||
|
controller-gen:
|
||||||
|
ifeq (, $(shell which controller-gen))
|
||||||
|
@{ \
|
||||||
|
set -e ;\
|
||||||
|
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
||||||
|
cd $$CONTROLLER_GEN_TMP_DIR ;\
|
||||||
|
go mod init tmp ;\
|
||||||
|
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.5.0 ;\
|
||||||
|
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
||||||
|
}
|
||||||
|
CONTROLLER_GEN=$(GOBIN)/controller-gen
|
||||||
|
else
|
||||||
|
CONTROLLER_GEN=$(shell which controller-gen)
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: go-lint
|
.PHONY: go-lint
|
||||||
|
|
||||||
# Run the golangci-lint tool
|
# Run the golangci-lint tool
|
||||||
@ -51,6 +68,11 @@ licensecheck:
|
|||||||
# Run all the linters
|
# Run all the linters
|
||||||
lint: licensecheck go-lint
|
lint: licensecheck go-lint
|
||||||
|
|
||||||
|
.PHONY: crd
|
||||||
|
|
||||||
|
# generates CRD using controller-gen
|
||||||
|
crd: controller-gen
|
||||||
|
${CONTROLLER_GEN} crd:crdVersions=v1 paths="./endpoint/..." output:crd:stdout > docs/contributing/crd-source/crd-manifest.yaml
|
||||||
|
|
||||||
# The verify target runs tasks similar to the CI tasks, but without code coverage
|
# The verify target runs tasks similar to the CI tasks, but without code coverage
|
||||||
.PHONY: verify test
|
.PHONY: verify test
|
||||||
@ -82,7 +104,7 @@ build.push/multiarch:
|
|||||||
for arch in $(ARCHS); do \
|
for arch in $(ARCHS); do \
|
||||||
image="$(IMAGE):$(VERSION)-$${arch}" ;\
|
image="$(IMAGE):$(VERSION)-$${arch}" ;\
|
||||||
# pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\
|
# pre-pull due to https://github.com/kubernetes-sigs/cluster-addons/pull/84/files ;\
|
||||||
docker pull $${arch}/alpine:3.12 ;\
|
docker pull $${arch}/alpine:3.13 ;\
|
||||||
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
|
DOCKER_BUILDKIT=1 docker build --rm --tag $${image} --build-arg VERSION="$(VERSION)" --build-arg ARCH="$${arch}" . ;\
|
||||||
docker push $${image} ;\
|
docker push $${image} ;\
|
||||||
arch_specific_tags+=( "--amend $${image}" ) ;\
|
arch_specific_tags+=( "--amend $${image}" ) ;\
|
||||||
|
@ -28,6 +28,7 @@ ExternalDNS' current release is `v0.7`. This version allows you to keep selected
|
|||||||
* [AWS Route 53](https://aws.amazon.com/route53/)
|
* [AWS Route 53](https://aws.amazon.com/route53/)
|
||||||
* [AWS Cloud Map](https://docs.aws.amazon.com/cloud-map/)
|
* [AWS Cloud Map](https://docs.aws.amazon.com/cloud-map/)
|
||||||
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
|
* [AzureDNS](https://azure.microsoft.com/en-us/services/dns)
|
||||||
|
* [BlueCat](https://bluecatnetworks.com)
|
||||||
* [CloudFlare](https://www.cloudflare.com/dns)
|
* [CloudFlare](https://www.cloudflare.com/dns)
|
||||||
* [RcodeZero](https://www.rcodezero.at/)
|
* [RcodeZero](https://www.rcodezero.at/)
|
||||||
* [DigitalOcean](https://www.digitalocean.com/products/networking)
|
* [DigitalOcean](https://www.digitalocean.com/products/networking)
|
||||||
@ -82,6 +83,7 @@ The following table clarifies the current status of the providers according to t
|
|||||||
| AWS Cloud Map | Beta | |
|
| AWS Cloud Map | Beta | |
|
||||||
| Akamai Edge DNS | Beta | |
|
| Akamai Edge DNS | Beta | |
|
||||||
| AzureDNS | Beta | |
|
| AzureDNS | Beta | |
|
||||||
|
| BlueCat | Alpha | @seanmalloy @vinny-sabatini |
|
||||||
| CloudFlare | Beta | |
|
| CloudFlare | Beta | |
|
||||||
| RcodeZero | Alpha | |
|
| RcodeZero | Alpha | |
|
||||||
| DigitalOcean | Alpha | |
|
| DigitalOcean | Alpha | |
|
||||||
|
@ -1,62 +1,94 @@
|
|||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.5.0
|
||||||
|
api-approved.kubernetes.io: "https://github.com/kubernetes-sigs/external-dns/pull/2007"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
|
||||||
api: externaldns
|
|
||||||
kubebuilder.k8s.io: 1.0.0
|
|
||||||
name: dnsendpoints.externaldns.k8s.io
|
name: dnsendpoints.externaldns.k8s.io
|
||||||
spec:
|
spec:
|
||||||
group: externaldns.k8s.io
|
group: externaldns.k8s.io
|
||||||
names:
|
names:
|
||||||
kind: DNSEndpoint
|
kind: DNSEndpoint
|
||||||
|
listKind: DNSEndpointList
|
||||||
plural: dnsendpoints
|
plural: dnsendpoints
|
||||||
|
singular: dnsendpoint
|
||||||
scope: Namespaced
|
scope: Namespaced
|
||||||
subresources:
|
versions:
|
||||||
status: {}
|
- name: v1alpha1
|
||||||
validation:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
properties:
|
properties:
|
||||||
apiVersion:
|
apiVersion:
|
||||||
type: string
|
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
kind:
|
type: string
|
||||||
type: string
|
kind:
|
||||||
metadata:
|
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
type: object
|
type: string
|
||||||
spec:
|
metadata:
|
||||||
properties:
|
type: object
|
||||||
endpoints:
|
spec:
|
||||||
items:
|
description: DNSEndpointSpec defines the desired state of DNSEndpoint
|
||||||
properties:
|
properties:
|
||||||
dnsName:
|
endpoints:
|
||||||
type: string
|
items:
|
||||||
labels:
|
description: Endpoint is a high-level way of a connection between a service and an IP
|
||||||
type: object
|
properties:
|
||||||
providerSpecific:
|
dnsName:
|
||||||
items:
|
description: The hostname of the DNS record
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
value:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
type: array
|
|
||||||
recordTTL:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
recordType:
|
|
||||||
type: string
|
|
||||||
targets:
|
|
||||||
items:
|
|
||||||
type: string
|
type: string
|
||||||
type: array
|
labels:
|
||||||
type: object
|
additionalProperties:
|
||||||
type: array
|
type: string
|
||||||
type: object
|
description: Labels stores labels defined for the Endpoint
|
||||||
status:
|
type: object
|
||||||
properties:
|
providerSpecific:
|
||||||
observedGeneration:
|
description: ProviderSpecific stores provider specific config
|
||||||
format: int64
|
items:
|
||||||
type: integer
|
description: ProviderSpecificProperty holds the name and value of a configuration which is specific to individual DNS providers
|
||||||
type: object
|
properties:
|
||||||
version: v1alpha1
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
recordTTL:
|
||||||
|
description: TTL for the record
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
recordType:
|
||||||
|
description: RecordType type of record, e.g. CNAME, A, SRV, TXT etc
|
||||||
|
type: string
|
||||||
|
setIdentifier:
|
||||||
|
description: Identifier to distinguish multiple records with the same name and type (e.g. Route53 records with routing policies other than 'simple')
|
||||||
|
type: string
|
||||||
|
targets:
|
||||||
|
description: The targets the DNS record points to
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: DNSEndpointStatus defines the observed state of DNSEndpoint
|
||||||
|
properties:
|
||||||
|
observedGeneration:
|
||||||
|
description: The generation observed by the external-dns controller.
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
|
25
docs/faq.md
25
docs/faq.md
@ -28,24 +28,7 @@ ExternalDNS can solve this for you as well.
|
|||||||
|
|
||||||
### Which DNS providers are supported?
|
### Which DNS providers are supported?
|
||||||
|
|
||||||
Currently, the following providers are supported:
|
Please check the [provider status table](https://github.com/kubernetes-sigs/external-dns#status-of-providers) for the list of supported providers and their status.
|
||||||
|
|
||||||
- Google Cloud DNS
|
|
||||||
- AWS Route 53
|
|
||||||
- AzureDNS
|
|
||||||
- CloudFlare
|
|
||||||
- DigitalOcean
|
|
||||||
- DNSimple
|
|
||||||
- Infoblox
|
|
||||||
- Dyn
|
|
||||||
- OpenStack Designate
|
|
||||||
- PowerDNS
|
|
||||||
- CoreDNS
|
|
||||||
- Exoscale
|
|
||||||
- Oracle Cloud Infrastructure DNS
|
|
||||||
- Linode DNS
|
|
||||||
- RFC2136
|
|
||||||
- TransIP
|
|
||||||
|
|
||||||
As stated in the README, we are currently looking for stable maintainers for those providers, to ensure that bugfixes and new features will be available for all of those.
|
As stated in the README, we are currently looking for stable maintainers for those providers, to ensure that bugfixes and new features will be available for all of those.
|
||||||
|
|
||||||
@ -279,11 +262,11 @@ If you need to filter only one specific source you have to run a separated exter
|
|||||||
|
|
||||||
### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both?
|
### How do I specify that I want the DNS record to point to either the Node's public or private IP when it has both?
|
||||||
|
|
||||||
If your Nodes have both public and private IP addresses, you might want to write DNS records with one or the other.
|
If your Nodes have both public and private IP addresses, you might want to write DNS records with one or the other.
|
||||||
For example, you may want to write a DNS record in a private zone that resolves to your Nodes' private IPs so that traffic never leaves your private network.
|
For example, you may want to write a DNS record in a private zone that resolves to your Nodes' private IPs so that traffic never leaves your private network.
|
||||||
|
|
||||||
To accomplish this, set this annotation on your service: `external-dns.alpha.kubernetes.io/access=private`
|
To accomplish this, set this annotation on your service: `external-dns.alpha.kubernetes.io/access=private`
|
||||||
Conversely, to force the public IP: `external-dns.alpha.kubernetes.io/access=public`
|
Conversely, to force the public IP: `external-dns.alpha.kubernetes.io/access=public`
|
||||||
|
|
||||||
If this annotation is not set, and the node has both public and private IP addresses, then the public IP will be used by default.
|
If this annotation is not set, and the node has both public and private IP addresses, then the public IP will be used by default.
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ Create an ingress resource manifest file.
|
|||||||
> For ingress objects ExternalDNS will create a DNS record based on the host specified for the ingress object.
|
> For ingress objects ExternalDNS will create a DNS record based on the host specified for the ingress object.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
@ -253,7 +253,7 @@ Create an ingress resource manifest file.
|
|||||||
> For ingress objects ExternalDNS will create a DNS record based on the host specified for the ingress object.
|
> For ingress objects ExternalDNS will create a DNS record based on the host specified for the ingress object.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: foo
|
name: foo
|
||||||
|
@ -375,7 +375,7 @@ spec:
|
|||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
@ -392,7 +392,7 @@ spec:
|
|||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
64
docs/tutorials/bluecat.md
Normal file
64
docs/tutorials/bluecat.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Setting up external-dns for BlueCat
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
Install the BlueCat Gateway product and deploy the [community gateway workflows](https://github.com/bluecatlabs/gateway-workflows).
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
Setup configuration file as k8s `Secret`.
|
||||||
|
```
|
||||||
|
cat << EOF > ~/bluecat.json
|
||||||
|
{
|
||||||
|
"gatewayHost": "https://bluecatgw.example.com",
|
||||||
|
"gatewayUsername": "user",
|
||||||
|
"GatewayPassword": "pass",
|
||||||
|
"dnsConfiguration": "Example",
|
||||||
|
"dnsView": "Internal",
|
||||||
|
"rootZone": "example.com"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
kubectl create secret generic bluecatconfig --from-file ~/bluecat.json -n bluecat-example
|
||||||
|
```
|
||||||
|
|
||||||
|
Setup up deployment/service account:
|
||||||
|
```
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
namespace: bluecat-example
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
namespace: bluecat-example
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: external-dns
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: external-dns
|
||||||
|
spec:
|
||||||
|
serviceAccountName: external-dns
|
||||||
|
volumes:
|
||||||
|
- name: bluecatconfig
|
||||||
|
secret:
|
||||||
|
secretName: bluecatconfig
|
||||||
|
containers:
|
||||||
|
- name: external-dns
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:$TAG # no released versions include the bluecat provider yet
|
||||||
|
volumeMounts:
|
||||||
|
- name: bluecatconfig
|
||||||
|
mountPath: "/etc/external-dns/"
|
||||||
|
readOnly: true
|
||||||
|
args:
|
||||||
|
- --log-level=debug
|
||||||
|
- --source=service
|
||||||
|
- --provider=bluecat
|
||||||
|
- --txt-owner-id=bluecat-example
|
||||||
|
- --bluecat-config-file=/etc/external-dns/bluecat.json
|
||||||
|
```
|
@ -194,7 +194,7 @@ minikube addons enable ingress
|
|||||||
## Testing ingress example
|
## Testing ingress example
|
||||||
```
|
```
|
||||||
$ cat ingress.yaml
|
$ cat ingress.yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
@ -111,7 +111,7 @@ Having `--dry-run=true` and `--log-level=debug` is a great way to see _exactly_
|
|||||||
Create a file called 'test-ingress.yaml' with the following contents:
|
Create a file called 'test-ingress.yaml' with the following contents:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: test-ingress
|
name: test-ingress
|
||||||
|
@ -104,7 +104,7 @@ subjects:
|
|||||||
Spin up a simple nginx HTTP server with the following spec (`kubectl apply -f`):
|
Spin up a simple nginx HTTP server with the following spec (`kubectl apply -f`):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
@ -211,7 +211,7 @@ $ curl nginx.external-dns-test.gcp.zalan.do
|
|||||||
Let's check that Ingress works as well. Create the following Ingress.
|
Let's check that Ingress works as well. Create the following Ingress.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
@ -460,7 +460,7 @@ $ kubectl annotate serviceaccount --namespace=external-dns external-dns \
|
|||||||
Create the following sample application to test that ExternalDNS works.
|
Create the following sample application to test that ExternalDNS works.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
101
docs/tutorials/gloo-proxy.md
Normal file
101
docs/tutorials/gloo-proxy.md
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# Configuring ExternalDNS to use the Gloo Proxy Source
|
||||||
|
This tutorial describes how to configure ExternalDNS to use the Gloo Proxy source.
|
||||||
|
It is meant to supplement the other provider-specific setup tutorials.
|
||||||
|
|
||||||
|
### Manifest (for clusters without RBAC enabled)
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
spec:
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: external-dns
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: external-dns
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: external-dns
|
||||||
|
# update this to the desired external-dns version
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||||
|
args:
|
||||||
|
- --source=gloo-proxy
|
||||||
|
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||||
|
- --provider=aws
|
||||||
|
- --registry=txt
|
||||||
|
- --txt-owner-id=my-identifier
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manifest (for clusters with RBAC enabled)
|
||||||
|
Could be change if you have mulitple sources
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["services","endpoints","pods"]
|
||||||
|
verbs: ["get","watch","list"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["nodes"]
|
||||||
|
verbs: ["list","watch"]
|
||||||
|
- apiGroups: ["gloo.solo.io"]
|
||||||
|
resources: ["proxies"]
|
||||||
|
verbs: ["get","watch","list"]
|
||||||
|
- apiGroups: ["gateway.solo.io"]
|
||||||
|
resources: ["virtualservices"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: external-dns-viewer
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: external-dns
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: external-dns
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: external-dns
|
||||||
|
spec:
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: external-dns
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: external-dns
|
||||||
|
spec:
|
||||||
|
serviceAccountName: external-dns
|
||||||
|
containers:
|
||||||
|
- name: external-dns
|
||||||
|
# update this to the desired external-dns version
|
||||||
|
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||||
|
args:
|
||||||
|
- --source=gloo-proxy
|
||||||
|
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||||
|
- --provider=aws
|
||||||
|
- --registry=txt
|
||||||
|
- --txt-owner-id=my-identifier
|
||||||
|
```
|
||||||
|
|
@ -138,7 +138,7 @@ default.
|
|||||||
Create the following Ingress to expose the echoserver application to the Internet.
|
Create the following Ingress to expose the echoserver application to the Internet.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -172,7 +172,7 @@ this Ingress object will only be fronting one backend Service, we might instead
|
|||||||
create the following:
|
create the following:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -205,7 +205,7 @@ and one AAAA record) for each hostname associated with the Ingress object.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -239,7 +239,7 @@ set to `nlb` then ExternalDNS will create an NLB instead of an ALB.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
@ -290,7 +290,7 @@ Use `--dry-run` if you want to be extra careful on the first run. Note, that you
|
|||||||
Create the following sample application to test that ExternalDNS works.
|
Create the following sample application to test that ExternalDNS works.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
@ -586,7 +586,7 @@ $ kubectl annotate serviceaccount --namespace=external-dns external-dns \
|
|||||||
Create the following sample application to test that ExternalDNS works.
|
Create the following sample application to test that ExternalDNS works.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
@ -292,7 +292,7 @@ For this setup to work, you've to create two Service definitions for your applic
|
|||||||
At first, create public Service definition:
|
At first, create public Service definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -313,7 +313,7 @@ spec:
|
|||||||
Then create private Service definition:
|
Then create private Service definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -334,7 +334,7 @@ spec:
|
|||||||
Additionally, you may leverage [cert-manager](https://github.com/jetstack/cert-manager) to automatically issue SSL certificates from [Let's Encrypt](https://letsencrypt.org/). To do that, request a certificate in public service definition:
|
Additionally, you may leverage [cert-manager](https://github.com/jetstack/cert-manager) to automatically issue SSL certificates from [Let's Encrypt](https://letsencrypt.org/). To do that, request a certificate in public service definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@ -363,7 +363,7 @@ spec:
|
|||||||
And reuse the requested certificate in private Service definition:
|
And reuse the requested certificate in private Service definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
@ -138,7 +138,7 @@ spec:
|
|||||||
## Testing ingress example
|
## Testing ingress example
|
||||||
```
|
```
|
||||||
$ cat ingress.yaml
|
$ cat ingress.yaml
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx
|
name: nginx
|
||||||
|
@ -94,7 +94,7 @@ spec:
|
|||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
---
|
---
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: my-ingress
|
name: my-ingress
|
||||||
@ -287,7 +287,7 @@ While `external-dns` was not developed or tested against Microsoft DNS, it can b
|
|||||||
|
|
||||||
1. Create a DNS zone
|
1. Create a DNS zone
|
||||||
2. Enable insecure dynamic updates for the zone
|
2. Enable insecure dynamic updates for the zone
|
||||||
3. Enable Zone Transfers from all servers
|
3. Enable Zone Transfers to all servers
|
||||||
|
|
||||||
#### `external-dns` configuration
|
#### `external-dns` configuration
|
||||||
|
|
||||||
@ -310,8 +310,10 @@ You'll want to configure `external-dns` similarly to the following:
|
|||||||
|
|
||||||
1. Create a DNS zone
|
1. Create a DNS zone
|
||||||
2. Enable secure dynamic updates for the zone
|
2. Enable secure dynamic updates for the zone
|
||||||
3. Enable Zone Transfers from all servers
|
3. Enable Zone Transfers to all servers
|
||||||
|
|
||||||
|
If you see any error messages which indicate that `external-dns` was somehow not able to fetch
|
||||||
|
existing DNS records from your DNS server, this could mean that you forgot about step 3.
|
||||||
|
|
||||||
#### Kerberos Configuration
|
#### Kerberos Configuration
|
||||||
|
|
||||||
@ -339,18 +341,20 @@ data:
|
|||||||
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
|
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
|
||||||
default_ccache_name = KEYRING:persistent:%{uid}
|
default_ccache_name = KEYRING:persistent:%{uid}
|
||||||
|
|
||||||
default_realm = YOURDOMAIN.COM
|
default_realm = YOUR-REALM.COM
|
||||||
|
|
||||||
[realms]
|
[realms]
|
||||||
YOURDOMAIN.COM = {
|
YOUR-REALM.COM = {
|
||||||
kdc = dc1.yourdomain.com
|
kdc = dc1.yourdomain.com
|
||||||
admin_server = dc1.yourdomain.com
|
admin_server = dc1.yourdomain.com
|
||||||
}
|
}
|
||||||
|
|
||||||
[domain_realm]
|
[domain_realm]
|
||||||
yourdomain.com = YOURDOMAIN.COM
|
yourdomain.com = YOUR-REALM.COM
|
||||||
.yourdomain.com = YOURDOMAIN.COM
|
.yourdomain.com = YOUR-REALM.COM
|
||||||
```
|
```
|
||||||
|
In most cases, the realm name will probably be the same as the domain name, so you can simply replace
|
||||||
|
`YOUR-REALM.COM` with something like `YOURDOMAIN.COM`.
|
||||||
|
|
||||||
Once the ConfigMap is created, the container `external-dns` container needs to be told to mount that ConfigMap as a volume at the default Kerberos configuration location. The pod spec should include a similar configuration to the following:
|
Once the ConfigMap is created, the container `external-dns` container needs to be told to mount that ConfigMap as a volume at the default Kerberos configuration location. The pod spec should include a similar configuration to the following:
|
||||||
|
|
||||||
@ -376,8 +380,7 @@ You'll want to configure `external-dns` similarly to the following:
|
|||||||
```text
|
```text
|
||||||
...
|
...
|
||||||
- --provider=rfc2136
|
- --provider=rfc2136
|
||||||
- --rfc2136-gss-tsig
|
- --rfc2136-host=dns-host.yourdomain.com
|
||||||
- --rfc2136-host=123.123.123.123
|
|
||||||
- --rfc2136-port=53
|
- --rfc2136-port=53
|
||||||
- --rfc2136-zone=your-zone.com
|
- --rfc2136-zone=your-zone.com
|
||||||
- --rfc2136-kerberos-username=your-domain-account
|
- --rfc2136-kerberos-username=your-domain-account
|
||||||
@ -385,4 +388,13 @@ You'll want to configure `external-dns` similarly to the following:
|
|||||||
- --rfc2136-kerberos-realm=your-domain.com
|
- --rfc2136-kerberos-realm=your-domain.com
|
||||||
- --rfc2136-tsig-axfr # needed to enable zone transfers, which is required for deletion of records.
|
- --rfc2136-tsig-axfr # needed to enable zone transfers, which is required for deletion of records.
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
As noted above, the `--rfc2136-kerberos-realm` flag is completely optional and won't be necessary in many cases.
|
||||||
|
Most likely, you will only need it if you see errors similar to this: `KRB Error: (68) KDC_ERR_WRONG_REALM Reserved for future use`.
|
||||||
|
|
||||||
|
The flag `--rfc2136-host` can be set to the host's domain name or IP address.
|
||||||
|
However, it also determines the name of the Kerberos principal which is used during authentication.
|
||||||
|
This means that Active Directory might only work if this is set to a specific domain name, possibly leading to errors like this:
|
||||||
|
`KDC_ERR_S_PRINCIPAL_UNKNOWN Server not found in Kerberos database`.
|
||||||
|
To fix this, try setting `--rfc2136-host` to the "actual" hostname of your DNS server.
|
||||||
|
@ -213,8 +213,12 @@ type DNSEndpointStatus struct {
|
|||||||
// DNSEndpoint is a contract that a user-specified CRD must implement to be used as a source for external-dns.
|
// DNSEndpoint is a contract that a user-specified CRD must implement to be used as a source for external-dns.
|
||||||
// The user-specified CRD should also have the status sub-resource.
|
// The user-specified CRD should also have the status sub-resource.
|
||||||
// +k8s:openapi-gen=true
|
// +k8s:openapi-gen=true
|
||||||
|
// +groupName=externaldns.k8s.io
|
||||||
// +kubebuilder:resource:path=dnsendpoints
|
// +kubebuilder:resource:path=dnsendpoints
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
|
// +versionName=v1alpha1
|
||||||
|
|
||||||
type DNSEndpoint struct {
|
type DNSEndpoint struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
@ -223,6 +227,7 @@ type DNSEndpoint struct {
|
|||||||
Status DNSEndpointStatus `json:"status,omitempty"`
|
Status DNSEndpointStatus `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
// DNSEndpointList is a list of DNSEndpoint objects
|
// DNSEndpointList is a list of DNSEndpoint objects
|
||||||
type DNSEndpointList struct {
|
type DNSEndpointList struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
4
go.mod
4
go.mod
@ -64,8 +64,8 @@ require (
|
|||||||
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
|
gopkg.in/ns1/ns1-go.v2 v2.0.0-20190322154155-0dafb5275fd1
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc
|
istio.io/api v0.0.0-20210128181506-0c4b8e54850f
|
||||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751
|
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02
|
||||||
k8s.io/api v0.18.8
|
k8s.io/api v0.18.8
|
||||||
k8s.io/apimachinery v0.18.8
|
k8s.io/apimachinery v0.18.8
|
||||||
k8s.io/client-go v0.18.8
|
k8s.io/client-go v0.18.8
|
||||||
|
24
go.sum
24
go.sum
@ -92,6 +92,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
|||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
|
||||||
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
|
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357 h1:3ynCSeUh9OtJLd/OzLapM1DLDv2g+0yyDdkLqSfZCaQ=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357 h1:3ynCSeUh9OtJLd/OzLapM1DLDv2g+0yyDdkLqSfZCaQ=
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
github.com/aliyun/alibaba-cloud-sdk-go v1.61.357/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||||
@ -127,8 +128,6 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
|
|||||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||||
github.com/bodgit/tsig v0.0.2 h1:seNt23SrPW8dkWoyRYzdeuqFEzr+lDc0dAJvo94xB8U=
|
github.com/bodgit/tsig v0.0.2 h1:seNt23SrPW8dkWoyRYzdeuqFEzr+lDc0dAJvo94xB8U=
|
||||||
github.com/bodgit/tsig v0.0.2/go.mod h1:0mYe0t9it36SOvDQyeFekc7bLtvljFz7H9vHS/nYbgc=
|
github.com/bodgit/tsig v0.0.2/go.mod h1:0mYe0t9it36SOvDQyeFekc7bLtvljFz7H9vHS/nYbgc=
|
||||||
github.com/bodgit/tsig v1.1.1 h1:SViReRa8KyaweqdJ3ojdYqIE3xDyJlR3G+6wAsSbLCo=
|
|
||||||
github.com/bodgit/tsig v1.1.1/go.mod h1:8LZ3Mn7AVZHH8GN2ArvzB7msHfLjoptWsdPEJRSw/uo=
|
|
||||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||||
@ -427,7 +426,9 @@ github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
|||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
|
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
||||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
@ -510,8 +511,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.1/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZ
|
|||||||
github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0=
|
github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0=
|
||||||
github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jinzhu/copier v0.1.0 h1:Vh8xALtH3rrKGB/XIRe5d0yCTHPZFauWPLvdpDAbi88=
|
|
||||||
github.com/jinzhu/copier v0.1.0/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
@ -599,8 +598,6 @@ github.com/maxatome/go-testdeep v1.4.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ys
|
|||||||
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
|
github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.1.6/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
|
||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
|
||||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1 h1:kZZmnTeY2r+88mDNCVV/uCXL2gG3rkVPTN9jcYfGQcI=
|
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1 h1:kZZmnTeY2r+88mDNCVV/uCXL2gG3rkVPTN9jcYfGQcI=
|
||||||
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.36-0.20210109083720-731b191cabd1/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
@ -690,6 +687,7 @@ github.com/openshift/api v0.0.0-20200605231317-fb2a6ca106ae/go.mod h1:l6TGeqJ92D
|
|||||||
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
|
github.com/openshift/build-machinery-go v0.0.0-20200424080330-082bf86082cc/go.mod h1:1CkcsT3aVebzRBzVTSbiKSkJMsC/CASqxesfqEMfJEc=
|
||||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
|
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73 h1:JePLt9EpNLF/30KsSsArrzxGWPaUIvYUt8Fwnw9wlgM=
|
||||||
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
|
github.com/openshift/client-go v0.0.0-20200608144219-584632b8fc73/go.mod h1:+66gk3dEqw9e+WoiXjJFzWlS1KGhj9ZRHi/RI/YG/ZM=
|
||||||
|
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b h1:it0YPE/evO6/m8t8wxis9KFI2F/aleOKsI6d9uz0cEk=
|
||||||
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b/go.mod h1:tNrEB5k8SI+g5kOlsCmL2ELASfpqEofI0+FLBgBdN08=
|
github.com/openshift/gssapi v0.0.0-20161010215902-5fb4217df13b/go.mod h1:tNrEB5k8SI+g5kOlsCmL2ELASfpqEofI0+FLBgBdN08=
|
||||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||||
@ -948,8 +946,6 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -1062,7 +1058,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -1073,9 +1068,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM
|
|||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
|
||||||
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -1225,10 +1217,10 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
|||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc h1:cR9GmbIBAz3FnY3tgs1SRn/uiznhtvG+mZBfD1p2vIA=
|
istio.io/api v0.0.0-20210128181506-0c4b8e54850f h1:zUFsawgPj5oI9p5cf91YCExRlxLIVsEkIunN9ODUSJs=
|
||||||
istio.io/api v0.0.0-20200529165953-72dad51d4ffc/go.mod h1:kyq3g5w42zl/AKlbzDGppYpGMQYMYMyZKeq0/eexML8=
|
istio.io/api v0.0.0-20210128181506-0c4b8e54850f/go.mod h1:88HN3o1fSD1jo+Z1WTLlJfMm9biopur6Ct9BFKjiB64=
|
||||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751 h1:yH62fTmV+5l1XVTWcomsc1jjH/oH9u/tTgn5NVmdIac=
|
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02 h1:ZA8Y2gKkKtEeYuKfqlEzIBDfU4IE5uIAdsXDeD41T9w=
|
||||||
istio.io/client-go v0.0.0-20200529172309-31c16ea3f751/go.mod h1:4SGvmmus5HNFdqQsIL+uQO1PbAhjQKtSjMTqwsvYHlg=
|
istio.io/client-go v0.0.0-20210128182905-ee2edd059e02/go.mod h1:oXMjFUWhxlReUSbg4i3GjKgOhSX1WgD68ZNlHQEcmQg=
|
||||||
istio.io/gogo-genproto v0.0.0-20190904133402-ee07f2785480/go.mod h1:uKtbae4K9k2rjjX4ToV0l6etglbc1i7gqQ94XdkshzY=
|
istio.io/gogo-genproto v0.0.0-20190904133402-ee07f2785480/go.mod h1:uKtbae4K9k2rjjX4ToV0l6etglbc1i7gqQ94XdkshzY=
|
||||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=
|
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a h1:w7zILua2dnYo9CxImhpNW4NE/8ZxEoc/wfBfHrhUhrE=
|
||||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs=
|
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a/go.mod h1:OzpAts7jljZceG4Vqi5/zXy/pOg1b209T3jb7Nv5wIs=
|
||||||
|
4
main.go
4
main.go
@ -39,6 +39,7 @@ import (
|
|||||||
"sigs.k8s.io/external-dns/provider/aws"
|
"sigs.k8s.io/external-dns/provider/aws"
|
||||||
"sigs.k8s.io/external-dns/provider/awssd"
|
"sigs.k8s.io/external-dns/provider/awssd"
|
||||||
"sigs.k8s.io/external-dns/provider/azure"
|
"sigs.k8s.io/external-dns/provider/azure"
|
||||||
|
"sigs.k8s.io/external-dns/provider/bluecat"
|
||||||
"sigs.k8s.io/external-dns/provider/cloudflare"
|
"sigs.k8s.io/external-dns/provider/cloudflare"
|
||||||
"sigs.k8s.io/external-dns/provider/coredns"
|
"sigs.k8s.io/external-dns/provider/coredns"
|
||||||
"sigs.k8s.io/external-dns/provider/designate"
|
"sigs.k8s.io/external-dns/provider/designate"
|
||||||
@ -120,6 +121,7 @@ func main() {
|
|||||||
CFUsername: cfg.CFUsername,
|
CFUsername: cfg.CFUsername,
|
||||||
CFPassword: cfg.CFPassword,
|
CFPassword: cfg.CFPassword,
|
||||||
ContourLoadBalancerService: cfg.ContourLoadBalancerService,
|
ContourLoadBalancerService: cfg.ContourLoadBalancerService,
|
||||||
|
GlooNamespace: cfg.GlooNamespace,
|
||||||
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
||||||
RequestTimeout: cfg.RequestTimeout,
|
RequestTimeout: cfg.RequestTimeout,
|
||||||
}
|
}
|
||||||
@ -194,6 +196,8 @@ func main() {
|
|||||||
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||||
case "azure-private-dns":
|
case "azure-private-dns":
|
||||||
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||||
|
case "bluecat":
|
||||||
|
p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, domainFilter, zoneIDFilter, cfg.DryRun)
|
||||||
case "vinyldns":
|
case "vinyldns":
|
||||||
p, err = vinyldns.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
p, err = vinyldns.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
||||||
case "vultr":
|
case "vultr":
|
||||||
|
@ -45,6 +45,7 @@ type Config struct {
|
|||||||
KubeConfig string
|
KubeConfig string
|
||||||
RequestTimeout time.Duration
|
RequestTimeout time.Duration
|
||||||
ContourLoadBalancerService string
|
ContourLoadBalancerService string
|
||||||
|
GlooNamespace string
|
||||||
SkipperRouteGroupVersion string
|
SkipperRouteGroupVersion string
|
||||||
Sources []string
|
Sources []string
|
||||||
Namespace string
|
Namespace string
|
||||||
@ -82,6 +83,7 @@ type Config struct {
|
|||||||
AzureResourceGroup string
|
AzureResourceGroup string
|
||||||
AzureSubscriptionID string
|
AzureSubscriptionID string
|
||||||
AzureUserAssignedIdentityClientID string
|
AzureUserAssignedIdentityClientID string
|
||||||
|
BluecatConfigFile string
|
||||||
CloudflareProxied bool
|
CloudflareProxied bool
|
||||||
CloudflareZonesPerPage int
|
CloudflareZonesPerPage int
|
||||||
CoreDNSPrefix string
|
CoreDNSPrefix string
|
||||||
@ -142,6 +144,7 @@ type Config struct {
|
|||||||
RFC2136Zone string
|
RFC2136Zone string
|
||||||
RFC2136Insecure bool
|
RFC2136Insecure bool
|
||||||
RFC2136GSSTSIG bool
|
RFC2136GSSTSIG bool
|
||||||
|
RFC2136KerberosRealm string
|
||||||
RFC2136KerberosUsername string
|
RFC2136KerberosUsername string
|
||||||
RFC2136KerberosPassword string
|
RFC2136KerberosPassword string
|
||||||
RFC2136KerberosRealm string
|
RFC2136KerberosRealm string
|
||||||
@ -168,6 +171,7 @@ var defaultConfig = &Config{
|
|||||||
KubeConfig: "",
|
KubeConfig: "",
|
||||||
RequestTimeout: time.Second * 30,
|
RequestTimeout: time.Second * 30,
|
||||||
ContourLoadBalancerService: "heptio-contour/contour",
|
ContourLoadBalancerService: "heptio-contour/contour",
|
||||||
|
GlooNamespace: "gloo-system",
|
||||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||||
Sources: nil,
|
Sources: nil,
|
||||||
Namespace: "",
|
Namespace: "",
|
||||||
@ -200,6 +204,7 @@ var defaultConfig = &Config{
|
|||||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||||
AzureResourceGroup: "",
|
AzureResourceGroup: "",
|
||||||
AzureSubscriptionID: "",
|
AzureSubscriptionID: "",
|
||||||
|
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||||
CloudflareProxied: false,
|
CloudflareProxied: false,
|
||||||
CloudflareZonesPerPage: 50,
|
CloudflareZonesPerPage: 50,
|
||||||
CoreDNSPrefix: "/skydns/",
|
CoreDNSPrefix: "/skydns/",
|
||||||
@ -256,6 +261,7 @@ var defaultConfig = &Config{
|
|||||||
RFC2136Zone: "",
|
RFC2136Zone: "",
|
||||||
RFC2136Insecure: false,
|
RFC2136Insecure: false,
|
||||||
RFC2136GSSTSIG: false,
|
RFC2136GSSTSIG: false,
|
||||||
|
RFC2136KerberosRealm: "",
|
||||||
RFC2136KerberosUsername: "",
|
RFC2136KerberosUsername: "",
|
||||||
RFC2136KerberosPassword: "",
|
RFC2136KerberosPassword: "",
|
||||||
RFC2136TSIGKeyName: "",
|
RFC2136TSIGKeyName: "",
|
||||||
@ -329,12 +335,14 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
// Flags related to Contour
|
// Flags related to Contour
|
||||||
app.Flag("contour-load-balancer", "The fully-qualified name of the Contour load balancer service. (default: heptio-contour/contour)").Default("heptio-contour/contour").StringVar(&cfg.ContourLoadBalancerService)
|
app.Flag("contour-load-balancer", "The fully-qualified name of the Contour load balancer service. (default: heptio-contour/contour)").Default("heptio-contour/contour").StringVar(&cfg.ContourLoadBalancerService)
|
||||||
|
|
||||||
|
// Flags related to Gloo
|
||||||
|
app.Flag("gloo-namespace", "Gloo namespace. (default: gloo-system)").Default("gloo-system").StringVar(&cfg.GlooNamespace)
|
||||||
|
|
||||||
// Flags related to Skipper RouteGroup
|
// Flags related to Skipper RouteGroup
|
||||||
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
|
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 sources
|
||||||
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, crd, empty, skipper-routegroup, openshift-route, ambassador-host)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host")
|
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)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host")
|
||||||
|
|
||||||
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
|
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("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 only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
||||||
@ -353,7 +361,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
app.Flag("managed-record-types", "Comma separated list of record types to manage (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
|
app.Flag("managed-record-types", "Comma separated list of record types to manage (default: A, CNAME) (supported records: CNAME, A, NS").Default("A", "CNAME").StringsVar(&cfg.ManagedDNSRecordTypes)
|
||||||
|
|
||||||
// Flags related to providers
|
// Flags related to providers
|
||||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy")
|
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, godaddy, google, azure, azure-dns, azure-private-dns, bluecat, cloudflare, rcodezero, digitalocean, hetzner, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, ovh, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns, scaleway, vultr, ultradns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "hetzner", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "ovh", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns", "scaleway", "vultr", "ultradns", "godaddy")
|
||||||
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
|
app.Flag("domain-filter", "Limit possible target zones by a domain suffix; specify multiple times for multiple domains (optional)").Default("").StringsVar(&cfg.DomainFilter)
|
||||||
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
|
app.Flag("exclude-domains", "Exclude subdomains (optional)").Default("").StringsVar(&cfg.ExcludeDomains)
|
||||||
app.Flag("zone-name-filter", "Filter target zones by zone domain (For now, only AzureDNS provider is using this flag); specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneNameFilter)
|
app.Flag("zone-name-filter", "Filter target zones by zone domain (For now, only AzureDNS provider is using this flag); specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneNameFilter)
|
||||||
@ -376,6 +384,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
app.Flag("azure-resource-group", "When using the Azure provider, override the Azure resource group to use (required when --provider=azure-private-dns)").Default(defaultConfig.AzureResourceGroup).StringVar(&cfg.AzureResourceGroup)
|
||||||
app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
|
app.Flag("azure-subscription-id", "When using the Azure provider, specify the Azure configuration file (required when --provider=azure-private-dns)").Default(defaultConfig.AzureSubscriptionID).StringVar(&cfg.AzureSubscriptionID)
|
||||||
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
|
app.Flag("azure-user-assigned-identity-client-id", "When using the Azure provider, override the client id of user assigned identity in config file (optional)").Default("").StringVar(&cfg.AzureUserAssignedIdentityClientID)
|
||||||
|
app.Flag("bluecat-config-file", "When using the Bluecat provider, specify the Bluecat configuration file (required when --provider=bluecat").Default(defaultConfig.BluecatConfigFile).StringVar(&cfg.BluecatConfigFile)
|
||||||
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
|
app.Flag("cloudflare-proxied", "When using the Cloudflare provider, specify if the proxy mode must be enabled (default: disabled)").BoolVar(&cfg.CloudflareProxied)
|
||||||
app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage)
|
app.Flag("cloudflare-zones-per-page", "When using the Cloudflare provider, specify how many zones per page listed, max. possible 50 (default: 50)").Default(strconv.Itoa(defaultConfig.CloudflareZonesPerPage)).IntVar(&cfg.CloudflareZonesPerPage)
|
||||||
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
app.Flag("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
||||||
|
@ -35,6 +35,7 @@ var (
|
|||||||
KubeConfig: "",
|
KubeConfig: "",
|
||||||
RequestTimeout: time.Second * 30,
|
RequestTimeout: time.Second * 30,
|
||||||
ContourLoadBalancerService: "heptio-contour/contour",
|
ContourLoadBalancerService: "heptio-contour/contour",
|
||||||
|
GlooNamespace: "gloo-system",
|
||||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||||
Sources: []string{"service"},
|
Sources: []string{"service"},
|
||||||
Namespace: "",
|
Namespace: "",
|
||||||
@ -61,6 +62,7 @@ var (
|
|||||||
AzureConfigFile: "/etc/kubernetes/azure.json",
|
AzureConfigFile: "/etc/kubernetes/azure.json",
|
||||||
AzureResourceGroup: "",
|
AzureResourceGroup: "",
|
||||||
AzureSubscriptionID: "",
|
AzureSubscriptionID: "",
|
||||||
|
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||||
CloudflareProxied: false,
|
CloudflareProxied: false,
|
||||||
CloudflareZonesPerPage: 50,
|
CloudflareZonesPerPage: 50,
|
||||||
CoreDNSPrefix: "/skydns/",
|
CoreDNSPrefix: "/skydns/",
|
||||||
@ -114,6 +116,7 @@ var (
|
|||||||
KubeConfig: "/some/path",
|
KubeConfig: "/some/path",
|
||||||
RequestTimeout: time.Second * 77,
|
RequestTimeout: time.Second * 77,
|
||||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||||
|
GlooNamespace: "gloo-not-system",
|
||||||
SkipperRouteGroupVersion: "zalando.org/v2",
|
SkipperRouteGroupVersion: "zalando.org/v2",
|
||||||
Sources: []string{"service", "ingress", "connector"},
|
Sources: []string{"service", "ingress", "connector"},
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
@ -142,6 +145,7 @@ var (
|
|||||||
AzureConfigFile: "azure.json",
|
AzureConfigFile: "azure.json",
|
||||||
AzureResourceGroup: "arg",
|
AzureResourceGroup: "arg",
|
||||||
AzureSubscriptionID: "arg",
|
AzureSubscriptionID: "arg",
|
||||||
|
BluecatConfigFile: "bluecat.json",
|
||||||
CloudflareProxied: true,
|
CloudflareProxied: true,
|
||||||
CloudflareZonesPerPage: 20,
|
CloudflareZonesPerPage: 20,
|
||||||
CoreDNSPrefix: "/coredns/",
|
CoreDNSPrefix: "/coredns/",
|
||||||
@ -220,6 +224,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--kubeconfig=/some/path",
|
"--kubeconfig=/some/path",
|
||||||
"--request-timeout=77s",
|
"--request-timeout=77s",
|
||||||
"--contour-load-balancer=heptio-contour-other/contour-other",
|
"--contour-load-balancer=heptio-contour-other/contour-other",
|
||||||
|
"--gloo-namespace=gloo-not-system",
|
||||||
"--skipper-routegroup-groupversion=zalando.org/v2",
|
"--skipper-routegroup-groupversion=zalando.org/v2",
|
||||||
"--source=service",
|
"--source=service",
|
||||||
"--source=ingress",
|
"--source=ingress",
|
||||||
@ -236,6 +241,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--azure-config-file=azure.json",
|
"--azure-config-file=azure.json",
|
||||||
"--azure-resource-group=arg",
|
"--azure-resource-group=arg",
|
||||||
"--azure-subscription-id=arg",
|
"--azure-subscription-id=arg",
|
||||||
|
"--bluecat-config-file=bluecat.json",
|
||||||
"--cloudflare-proxied",
|
"--cloudflare-proxied",
|
||||||
"--cloudflare-zones-per-page=20",
|
"--cloudflare-zones-per-page=20",
|
||||||
"--coredns-prefix=/coredns/",
|
"--coredns-prefix=/coredns/",
|
||||||
@ -317,6 +323,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_KUBECONFIG": "/some/path",
|
"EXTERNAL_DNS_KUBECONFIG": "/some/path",
|
||||||
"EXTERNAL_DNS_REQUEST_TIMEOUT": "77s",
|
"EXTERNAL_DNS_REQUEST_TIMEOUT": "77s",
|
||||||
"EXTERNAL_DNS_CONTOUR_LOAD_BALANCER": "heptio-contour-other/contour-other",
|
"EXTERNAL_DNS_CONTOUR_LOAD_BALANCER": "heptio-contour-other/contour-other",
|
||||||
|
"EXTERNAL_DNS_GLOO_NAMESPACE": "gloo-not-system",
|
||||||
"EXTERNAL_DNS_SKIPPER_ROUTEGROUP_GROUPVERSION": "zalando.org/v2",
|
"EXTERNAL_DNS_SKIPPER_ROUTEGROUP_GROUPVERSION": "zalando.org/v2",
|
||||||
"EXTERNAL_DNS_SOURCE": "service\ningress\nconnector",
|
"EXTERNAL_DNS_SOURCE": "service\ningress\nconnector",
|
||||||
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
||||||
@ -331,6 +338,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
"EXTERNAL_DNS_AZURE_CONFIG_FILE": "azure.json",
|
||||||
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
||||||
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
||||||
|
"EXTERNAL_DNS_BLUECAT_CONFIG_FILE": "bluecat.json",
|
||||||
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
||||||
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
||||||
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
||||||
|
6
provider/bluecat/OWNERS
Normal file
6
provider/bluecat/OWNERS
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
approvers:
|
||||||
|
- seanmalloy
|
||||||
|
- vinny-sabatini
|
||||||
|
reviewers:
|
||||||
|
- seanmalloy
|
||||||
|
- vinny-sabatini
|
969
provider/bluecat/bluecat.go
Normal file
969
provider/bluecat/bluecat.go
Normal file
@ -0,0 +1,969 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Ensure we have proper error handling/logging for API calls to Bluecat. getBluecatGatewayToken has a good example of this
|
||||||
|
|
||||||
|
package bluecat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
"sigs.k8s.io/external-dns/plan"
|
||||||
|
"sigs.k8s.io/external-dns/provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bluecatConfig struct {
|
||||||
|
GatewayHost string `json:"gatewayHost"`
|
||||||
|
GatewayUsername string `json:"gatewayUsername"`
|
||||||
|
GatewayPassword string `json:"gatewayPassword"`
|
||||||
|
DNSConfiguration string `json:"dnsConfiguration"`
|
||||||
|
View string `json:"dnsView"`
|
||||||
|
RootZone string `json:"rootZone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluecatProvider implements the DNS provider for Bluecat DNS
|
||||||
|
type BluecatProvider struct {
|
||||||
|
provider.BaseProvider
|
||||||
|
domainFilter endpoint.DomainFilter
|
||||||
|
zoneIDFilter provider.ZoneIDFilter
|
||||||
|
dryRun bool
|
||||||
|
RootZone string
|
||||||
|
DNSConfiguration string
|
||||||
|
View string
|
||||||
|
gatewayClient GatewayClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type GatewayClient interface {
|
||||||
|
getBluecatZones(zoneName string) ([]BluecatZone, error)
|
||||||
|
getHostRecords(zone string, records *[]BluecatHostRecord) error
|
||||||
|
getCNAMERecords(zone string, records *[]BluecatCNAMERecord) error
|
||||||
|
getHostRecord(name string, record *BluecatHostRecord) error
|
||||||
|
getCNAMERecord(name string, record *BluecatCNAMERecord) error
|
||||||
|
createHostRecord(zone string, req *bluecatCreateHostRecordRequest) (res interface{}, err error)
|
||||||
|
createCNAMERecord(zone string, req *bluecatCreateCNAMERecordRequest) (res interface{}, err error)
|
||||||
|
deleteHostRecord(name string) (err error)
|
||||||
|
deleteCNAMERecord(name string) (err error)
|
||||||
|
buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error)
|
||||||
|
getTXTRecords(zone string, records *[]BluecatTXTRecord) error
|
||||||
|
getTXTRecord(name string, record *BluecatTXTRecord) error
|
||||||
|
createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (res interface{}, err error)
|
||||||
|
deleteTXTRecord(name string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatewayClientConfig defines new client on bluecat gateway
|
||||||
|
type GatewayClientConfig struct {
|
||||||
|
Cookie http.Cookie
|
||||||
|
Token string
|
||||||
|
Host string
|
||||||
|
DNSConfiguration string
|
||||||
|
View string
|
||||||
|
RootZone string
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluecatZone defines a zone to hold records
|
||||||
|
type BluecatZone struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Properties string `json:"properties"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluecatHostRecord defines dns Host record
|
||||||
|
type BluecatHostRecord struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Properties string `json:"properties"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluecatCNAMERecord defines dns CNAME record
|
||||||
|
type BluecatCNAMERecord struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Properties string `json:"properties"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluecatTXTRecord defines dns TXT record
|
||||||
|
type BluecatTXTRecord struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatRecordSet struct {
|
||||||
|
obj interface{}
|
||||||
|
res interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatCreateHostRecordRequest struct {
|
||||||
|
AbsoluteName string `json:"absolute_name"`
|
||||||
|
IP4Address string `json:"ip4_address"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
Properties string `json:"properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatCreateCNAMERecordRequest struct {
|
||||||
|
AbsoluteName string `json:"absolute_name"`
|
||||||
|
LinkedRecord string `json:"linked_record"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
Properties string `json:"properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatCreateTXTRecordRequest struct {
|
||||||
|
AbsoluteName string `json:"absolute_name"`
|
||||||
|
Text string `json:"txt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBluecatProvider creates a new Bluecat provider.
|
||||||
|
//
|
||||||
|
// Returns a pointer to the provider or an error if a provider could not be created.
|
||||||
|
func NewBluecatProvider(configFile string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool) (*BluecatProvider, error) {
|
||||||
|
contents, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read Bluecat config file %v", configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := bluecatConfig{}
|
||||||
|
err = json.Unmarshal(contents, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read Bluecat config file %v", configFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, cookie, err := getBluecatGatewayToken(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to get API token from Bluecat Gateway")
|
||||||
|
}
|
||||||
|
gatewayClient := NewGatewayClient(cookie, token, cfg.GatewayHost, cfg.DNSConfiguration, cfg.View, cfg.RootZone)
|
||||||
|
|
||||||
|
provider := &BluecatProvider{
|
||||||
|
domainFilter: domainFilter,
|
||||||
|
zoneIDFilter: zoneIDFilter,
|
||||||
|
dryRun: dryRun,
|
||||||
|
gatewayClient: gatewayClient,
|
||||||
|
DNSConfiguration: cfg.DNSConfiguration,
|
||||||
|
View: cfg.View,
|
||||||
|
RootZone: cfg.RootZone,
|
||||||
|
}
|
||||||
|
return provider, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGatewayClient creates and returns a new Bluecat gateway client
|
||||||
|
func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration, view, rootZone string) GatewayClientConfig {
|
||||||
|
// Right now the Bluecat gateway doesn't seem to have a way to get the root zone from the API. If the user
|
||||||
|
// doesn't provide one via the config file we'll assume it's 'com'
|
||||||
|
if rootZone == "" {
|
||||||
|
rootZone = "com"
|
||||||
|
}
|
||||||
|
return GatewayClientConfig{
|
||||||
|
Cookie: cookie,
|
||||||
|
Token: token,
|
||||||
|
Host: gatewayHost,
|
||||||
|
DNSConfiguration: dnsConfiguration,
|
||||||
|
View: view,
|
||||||
|
RootZone: rootZone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Records fetches Host, CNAME, and TXT records from bluecat gateway
|
||||||
|
func (p *BluecatProvider) Records(ctx context.Context) (endpoints []*endpoint.Endpoint, err error) {
|
||||||
|
zones, err := p.zones()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not fetch zones")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range zones {
|
||||||
|
log.Debugf("fetching records from zone '%s'", zone)
|
||||||
|
var resH []BluecatHostRecord
|
||||||
|
err = p.gatewayClient.getHostRecords(zone, &resH)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not fetch host records for zone: %v", zone)
|
||||||
|
}
|
||||||
|
for _, rec := range resH {
|
||||||
|
propMap := splitProperties(rec.Properties)
|
||||||
|
ips := strings.Split(propMap["addresses"], ",")
|
||||||
|
for _, ip := range ips {
|
||||||
|
ep := endpoint.NewEndpoint(propMap["absoluteName"], endpoint.RecordTypeA, ip)
|
||||||
|
endpoints = append(endpoints, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resC []BluecatCNAMERecord
|
||||||
|
err = p.gatewayClient.getCNAMERecords(zone, &resC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not fetch CNAME records for zone: %v", zone)
|
||||||
|
}
|
||||||
|
for _, rec := range resC {
|
||||||
|
propMap := splitProperties(rec.Properties)
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpoint(propMap["absoluteName"], endpoint.RecordTypeCNAME, propMap["linkedRecordName"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
var resT []BluecatTXTRecord
|
||||||
|
err = p.gatewayClient.getTXTRecords(zone, &resT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not fetch TXT records for zone: %v", zone)
|
||||||
|
}
|
||||||
|
for _, rec := range resT {
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpoint(rec.Name, endpoint.RecordTypeTXT, rec.Text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("fetched %d records from Bluecat", len(endpoints))
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyChanges updates necessary zones and replaces old records with new ones
|
||||||
|
//
|
||||||
|
// Returns nil upon success and err is there is an error
|
||||||
|
func (p *BluecatProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||||
|
zones, err := p.zones()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("zones is: %+v\n", zones)
|
||||||
|
log.Infof("changes: %+v\n", changes)
|
||||||
|
created, deleted := p.mapChanges(zones, changes)
|
||||||
|
log.Infof("created: %+v\n", created)
|
||||||
|
log.Infof("deleted: %+v\n", deleted)
|
||||||
|
p.deleteRecords(deleted)
|
||||||
|
p.createRecords(created)
|
||||||
|
|
||||||
|
// TODO: add bluecat deploy API call here
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatChangeMap map[string][]*endpoint.Endpoint
|
||||||
|
|
||||||
|
func (p *BluecatProvider) mapChanges(zones []string, changes *plan.Changes) (bluecatChangeMap, bluecatChangeMap) {
|
||||||
|
created := bluecatChangeMap{}
|
||||||
|
deleted := bluecatChangeMap{}
|
||||||
|
|
||||||
|
mapChange := func(changeMap bluecatChangeMap, change *endpoint.Endpoint) {
|
||||||
|
zone := p.findZone(zones, change.DNSName)
|
||||||
|
if zone == "" {
|
||||||
|
log.Debugf("ignoring changes to '%s' because a suitable Bluecat DNS zone was not found", change.DNSName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
changeMap[zone] = append(changeMap[zone], change)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, change := range changes.Delete {
|
||||||
|
mapChange(deleted, change)
|
||||||
|
}
|
||||||
|
for _, change := range changes.UpdateOld {
|
||||||
|
mapChange(deleted, change)
|
||||||
|
}
|
||||||
|
for _, change := range changes.Create {
|
||||||
|
mapChange(created, change)
|
||||||
|
}
|
||||||
|
for _, change := range changes.UpdateNew {
|
||||||
|
mapChange(created, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
return created, deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// findZone finds the most specific matching zone for a given record 'name' from a list of all zones
|
||||||
|
func (p *BluecatProvider) findZone(zones []string, name string) string {
|
||||||
|
var result string
|
||||||
|
|
||||||
|
for _, zone := range zones {
|
||||||
|
if strings.HasSuffix(name, "."+zone) {
|
||||||
|
if result == "" || len(zone) > len(result) {
|
||||||
|
result = zone
|
||||||
|
}
|
||||||
|
} else if strings.EqualFold(name, zone) {
|
||||||
|
if result == "" || len(zone) > len(result) {
|
||||||
|
result = zone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BluecatProvider) zones() ([]string, error) {
|
||||||
|
log.Debugf("retrieving Bluecat zones for configuration: %s, view: %s", p.DNSConfiguration, p.View)
|
||||||
|
var zones []string
|
||||||
|
|
||||||
|
zonelist, err := p.gatewayClient.getBluecatZones(p.RootZone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range zonelist {
|
||||||
|
if !p.domainFilter.Match(zone.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: match to absoluteName(string) not Id(int)
|
||||||
|
if !p.zoneIDFilter.Match(strconv.Itoa(zone.ID)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneProps := splitProperties(zone.Properties)
|
||||||
|
|
||||||
|
zones = append(zones, zoneProps["absoluteName"])
|
||||||
|
}
|
||||||
|
log.Debugf("found %d zones", len(zones))
|
||||||
|
return zones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BluecatProvider) createRecords(created bluecatChangeMap) {
|
||||||
|
for zone, endpoints := range created {
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
if p.dryRun {
|
||||||
|
log.Infof("would create %s record named '%s' to '%s' for Bluecat DNS zone '%s'.",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
ep.Targets,
|
||||||
|
zone,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("creating %s record named '%s' to '%s' for Bluecat DNS zone '%s'.",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
ep.Targets,
|
||||||
|
zone,
|
||||||
|
)
|
||||||
|
|
||||||
|
recordSet, err := p.recordSet(ep, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(
|
||||||
|
"Failed to retrieve %s record named '%s' to '%s' for Bluecat DNS zone '%s': %v",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
ep.Targets,
|
||||||
|
zone,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var response interface{}
|
||||||
|
switch ep.RecordType {
|
||||||
|
case endpoint.RecordTypeA:
|
||||||
|
response, err = p.gatewayClient.createHostRecord(zone, recordSet.obj.(*bluecatCreateHostRecordRequest))
|
||||||
|
case endpoint.RecordTypeCNAME:
|
||||||
|
response, err = p.gatewayClient.createCNAMERecord(zone, recordSet.obj.(*bluecatCreateCNAMERecordRequest))
|
||||||
|
case endpoint.RecordTypeTXT:
|
||||||
|
response, err = p.gatewayClient.createTXTRecord(zone, recordSet.obj.(*bluecatCreateTXTRecordRequest))
|
||||||
|
}
|
||||||
|
log.Debugf("Response from create: %v", response)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(
|
||||||
|
"Failed to create %s record named '%s' to '%s' for Bluecat DNS zone '%s': %v",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
ep.Targets,
|
||||||
|
zone,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BluecatProvider) deleteRecords(deleted bluecatChangeMap) {
|
||||||
|
// run deletions first
|
||||||
|
for zone, endpoints := range deleted {
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
if p.dryRun {
|
||||||
|
log.Infof("would delete %s record named '%s' for Bluecat DNS zone '%s'.",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
zone,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
log.Infof("deleting %s record named '%s' for Bluecat DNS zone '%s'.",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
zone,
|
||||||
|
)
|
||||||
|
|
||||||
|
recordSet, err := p.recordSet(ep, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(
|
||||||
|
"Failed to retrieve %s record named '%s' to '%s' for Bluecat DNS zone '%s': %v",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
ep.Targets,
|
||||||
|
zone,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ep.RecordType {
|
||||||
|
case endpoint.RecordTypeA:
|
||||||
|
for _, record := range *recordSet.res.(*[]BluecatHostRecord) {
|
||||||
|
err = p.gatewayClient.deleteHostRecord(record.Name)
|
||||||
|
}
|
||||||
|
case endpoint.RecordTypeCNAME:
|
||||||
|
for _, record := range *recordSet.res.(*[]BluecatCNAMERecord) {
|
||||||
|
err = p.gatewayClient.deleteCNAMERecord(record.Name)
|
||||||
|
}
|
||||||
|
case endpoint.RecordTypeTXT:
|
||||||
|
for _, record := range *recordSet.res.(*[]BluecatTXTRecord) {
|
||||||
|
err = p.gatewayClient.deleteTXTRecord(record.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to delete %s record named '%s' for Bluecat DNS zone '%s': %v",
|
||||||
|
ep.RecordType,
|
||||||
|
ep.DNSName,
|
||||||
|
zone,
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BluecatProvider) recordSet(ep *endpoint.Endpoint, getObject bool) (recordSet bluecatRecordSet, err error) {
|
||||||
|
switch ep.RecordType {
|
||||||
|
case endpoint.RecordTypeA:
|
||||||
|
var res []BluecatHostRecord
|
||||||
|
// TODO Allow configurable properties/ttl
|
||||||
|
obj := bluecatCreateHostRecordRequest{
|
||||||
|
AbsoluteName: ep.DNSName,
|
||||||
|
IP4Address: ep.Targets[0],
|
||||||
|
TTL: 0,
|
||||||
|
Properties: "",
|
||||||
|
}
|
||||||
|
if getObject {
|
||||||
|
var record BluecatHostRecord
|
||||||
|
err = p.gatewayClient.getHostRecord(ep.DNSName, &record)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = append(res, record)
|
||||||
|
}
|
||||||
|
recordSet = bluecatRecordSet{
|
||||||
|
obj: &obj,
|
||||||
|
res: &res,
|
||||||
|
}
|
||||||
|
case endpoint.RecordTypeCNAME:
|
||||||
|
var res []BluecatCNAMERecord
|
||||||
|
obj := bluecatCreateCNAMERecordRequest{
|
||||||
|
AbsoluteName: ep.DNSName,
|
||||||
|
LinkedRecord: ep.Targets[0],
|
||||||
|
TTL: 0,
|
||||||
|
Properties: "",
|
||||||
|
}
|
||||||
|
if getObject {
|
||||||
|
var record BluecatCNAMERecord
|
||||||
|
err = p.gatewayClient.getCNAMERecord(ep.DNSName, &record)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = append(res, record)
|
||||||
|
}
|
||||||
|
recordSet = bluecatRecordSet{
|
||||||
|
obj: &obj,
|
||||||
|
res: &res,
|
||||||
|
}
|
||||||
|
case endpoint.RecordTypeTXT:
|
||||||
|
var res []BluecatTXTRecord
|
||||||
|
obj := bluecatCreateTXTRecordRequest{
|
||||||
|
AbsoluteName: ep.DNSName,
|
||||||
|
Text: ep.Targets[0],
|
||||||
|
}
|
||||||
|
if getObject {
|
||||||
|
var record BluecatTXTRecord
|
||||||
|
err = p.gatewayClient.getTXTRecord(ep.DNSName, &record)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = append(res, record)
|
||||||
|
}
|
||||||
|
recordSet = bluecatRecordSet{
|
||||||
|
obj: &obj,
|
||||||
|
res: &res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getBluecatGatewayToken retrieves a Bluecat Gateway API token.
|
||||||
|
func getBluecatGatewayToken(cfg bluecatConfig) (string, http.Cookie, error) {
|
||||||
|
body, err := json.Marshal(map[string]string{
|
||||||
|
"username": cfg.GatewayUsername,
|
||||||
|
"password": cfg.GatewayPassword,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", http.Cookie{}, errors.Wrap(err, "could not unmarshal credentials for bluecat gateway config")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // ignore self-signed SSL cert check
|
||||||
|
}}
|
||||||
|
|
||||||
|
resp, err := c.Post(cfg.GatewayHost+"/rest_login", "application/json", bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return "", http.Cookie{}, errors.Wrap(err, "error obtaining API token from bluecat gateway")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
details, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
return "", http.Cookie{}, errors.Errorf("got HTTP response code %v, detailed message: %v", resp.StatusCode, string(details))
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", http.Cookie{}, errors.Wrap(err, "error reading get_token response from bluecat gateway")
|
||||||
|
}
|
||||||
|
|
||||||
|
resJSON := map[string]string{}
|
||||||
|
err = json.Unmarshal(res, &resJSON)
|
||||||
|
if err != nil {
|
||||||
|
return "", http.Cookie{}, errors.Wrap(err, "error unmarshaling json response (auth) from bluecat gateway")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example response: {"access_token": "BAMAuthToken: abc123"}
|
||||||
|
// We only care about the actual token string - i.e. abc123
|
||||||
|
// The gateway also creates a cookie as part of the response. This seems to be the actual auth mechanism, at least
|
||||||
|
// for now.
|
||||||
|
return strings.Split(resJSON["access_token"], " ")[1], *resp.Cookies()[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getBluecatZones(zoneName string) ([]BluecatZone, error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
zonePath := expandZone(zoneName)
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving zone(s) from gateway: %v, %v", url, zoneName)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
zones := []BluecatZone{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&zones)
|
||||||
|
|
||||||
|
// Bluecat Gateway only returns subzones one level deeper than the provided zone
|
||||||
|
// so this recursion is needed to traverse subzones until none are returned
|
||||||
|
for _, zone := range zones {
|
||||||
|
zoneProps := splitProperties(zone.Properties)
|
||||||
|
subZones, err := c.getBluecatZones(zoneProps["absoluteName"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error retrieving subzones from gateway: %v", zoneName)
|
||||||
|
}
|
||||||
|
zones = append(zones, subZones...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return zones, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getHostRecords(zone string, records *[]BluecatHostRecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "host_records/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(records)
|
||||||
|
log.Debugf("Get Host Records Response: %v", records)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getCNAMERecords(zone string, records *[]BluecatCNAMERecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "cname_records/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(records)
|
||||||
|
log.Debugf("Get CName Records Response: %v", records)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getTXTRecords(zone string, records *[]BluecatTXTRecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "text_records/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
log.Debugf("Request: %v", req)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", zone)
|
||||||
|
}
|
||||||
|
log.Debugf("Get Txt Records response: %v", resp)
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(records)
|
||||||
|
log.Debugf("Get TXT Records Body: %v", records)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getHostRecord(name string, record *BluecatHostRecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"host_records/" + name + "/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error building http request: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(record)
|
||||||
|
log.Debugf("Get Host Record Response: %v", record)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getCNAMERecord(name string, record *BluecatCNAMERecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"cname_records/" + name + "/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error building http request: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(record)
|
||||||
|
log.Debugf("Get CName Record Response: %v", record)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) getTXTRecord(name string, record *BluecatTXTRecord) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"text_records/" + name + "/"
|
||||||
|
req, err := c.buildHTTPRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error retrieving record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
json.NewDecoder(resp.Body).Decode(record)
|
||||||
|
log.Debugf("Get TXT Record Response: %v", record)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) createHostRecord(zone string, req *bluecatCreateHostRecordRequest) (res interface{}, err error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "host_records/"
|
||||||
|
body, _ := json.Marshal(req)
|
||||||
|
hreq, err := c.buildHTTPRequest("POST", url, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
hreq.Header.Add("Content-Type", "application/json")
|
||||||
|
res, err = client.Do(hreq)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) createCNAMERecord(zone string, req *bluecatCreateCNAMERecordRequest) (res interface{}, err error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "cname_records/"
|
||||||
|
body, _ := json.Marshal(req)
|
||||||
|
|
||||||
|
hreq, err := c.buildHTTPRequest("POST", url, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
hreq.Header.Add("Content-Type", "application/json")
|
||||||
|
res, err = client.Do(hreq)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (interface{}, error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
zonePath := expandZone(zone)
|
||||||
|
// Remove the trailing 'zones/'
|
||||||
|
zonePath = strings.TrimSuffix(zonePath, "zones/")
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/views/" + c.View + "/" + zonePath + "text_records/"
|
||||||
|
body, _ := json.Marshal(req)
|
||||||
|
hreq, err := c.buildHTTPRequest("POST", url, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hreq.Header.Add("Content-Type", "application/json")
|
||||||
|
res, err := client.Do(hreq)
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) deleteHostRecord(name string) (err error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"host_records/" + name + "/"
|
||||||
|
req, err := c.buildHTTPRequest("DELETE", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error building http request: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) deleteCNAMERecord(name string) (err error) {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"cname_records/" + name + "/"
|
||||||
|
req, err := c.buildHTTPRequest("DELETE", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error building http request: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GatewayClientConfig) deleteTXTRecord(name string) error {
|
||||||
|
transportCfg := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //ignore self-signed SSL cert check
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transportCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration +
|
||||||
|
"/views/" + c.View + "/" +
|
||||||
|
"text_records/" + name + "/"
|
||||||
|
|
||||||
|
req, err := c.buildHTTPRequest("DELETE", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error building http request")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error deleting record(s) from gateway: %v", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//buildHTTPRequest builds a standard http Request and adds authentication headers required by Bluecat Gateway
|
||||||
|
func (c GatewayClientConfig) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||||
|
req, err := http.NewRequest(method, url, body)
|
||||||
|
req.Header.Add("Accept", "application/json")
|
||||||
|
req.Header.Add("Authorization", "Basic "+c.Token)
|
||||||
|
req.AddCookie(&c.Cookie)
|
||||||
|
return req, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//splitProperties is a helper function to break a '|' separated string into key/value pairs
|
||||||
|
// i.e. "foo=bar|baz=mop"
|
||||||
|
func splitProperties(props string) map[string]string {
|
||||||
|
propMap := make(map[string]string)
|
||||||
|
|
||||||
|
// remove trailing | character before we split
|
||||||
|
props = strings.TrimSuffix(props, "|")
|
||||||
|
|
||||||
|
splits := strings.Split(props, "|")
|
||||||
|
for _, pair := range splits {
|
||||||
|
items := strings.Split(pair, "=")
|
||||||
|
propMap[items[0]] = items[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return propMap
|
||||||
|
}
|
||||||
|
|
||||||
|
//expandZone takes an absolute domain name such as 'example.com' and returns a zone hierarchy used by Bluecat Gateway,
|
||||||
|
//such as '/zones/com/zones/example/zones/'
|
||||||
|
func expandZone(zone string) string {
|
||||||
|
ze := "zones/"
|
||||||
|
parts := strings.Split(zone, ".")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
last := len(parts) - 1
|
||||||
|
for i := range parts {
|
||||||
|
ze = ze + parts[last-i] + "/zones/"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ze = ze + zone + "/zones/"
|
||||||
|
}
|
||||||
|
return ze
|
||||||
|
}
|
390
provider/bluecat/bluecat_test.go
Normal file
390
provider/bluecat/bluecat_test.go
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bluecat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
"sigs.k8s.io/external-dns/internal/testutils"
|
||||||
|
"sigs.k8s.io/external-dns/plan"
|
||||||
|
"sigs.k8s.io/external-dns/provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockGatewayClient struct {
|
||||||
|
mockBluecatZones *[]BluecatZone
|
||||||
|
mockBluecatHosts *[]BluecatHostRecord
|
||||||
|
mockBluecatCNAMEs *[]BluecatCNAMERecord
|
||||||
|
mockBluecatTXTs *[]BluecatTXTRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
type Changes struct {
|
||||||
|
// Records that need to be created
|
||||||
|
Create []*endpoint.Endpoint
|
||||||
|
// Records that need to be updated (current data)
|
||||||
|
UpdateOld []*endpoint.Endpoint
|
||||||
|
// Records that need to be updated (desired data)
|
||||||
|
UpdateNew []*endpoint.Endpoint
|
||||||
|
// Records that need to be deleted
|
||||||
|
Delete []*endpoint.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g mockGatewayClient) getBluecatZones(zoneName string) ([]BluecatZone, error) {
|
||||||
|
return *g.mockBluecatZones, nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getHostRecords(zone string, records *[]BluecatHostRecord) error {
|
||||||
|
*records = *g.mockBluecatHosts
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getCNAMERecords(zone string, records *[]BluecatCNAMERecord) error {
|
||||||
|
*records = *g.mockBluecatCNAMEs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getHostRecord(name string, record *BluecatHostRecord) error {
|
||||||
|
for _, currentRecord := range *g.mockBluecatHosts {
|
||||||
|
if currentRecord.Name == strings.Split(name, ".")[0] {
|
||||||
|
*record = currentRecord
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getCNAMERecord(name string, record *BluecatCNAMERecord) error {
|
||||||
|
for _, currentRecord := range *g.mockBluecatCNAMEs {
|
||||||
|
if currentRecord.Name == strings.Split(name, ".")[0] {
|
||||||
|
*record = currentRecord
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) createHostRecord(zone string, req *bluecatCreateHostRecordRequest) (res interface{}, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) createCNAMERecord(zone string, req *bluecatCreateCNAMERecordRequest) (res interface{}, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) deleteHostRecord(name string) (err error) {
|
||||||
|
*g.mockBluecatHosts = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) deleteCNAMERecord(name string) (err error) {
|
||||||
|
*g.mockBluecatCNAMEs = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getTXTRecords(zone string, records *[]BluecatTXTRecord) error {
|
||||||
|
*records = *g.mockBluecatTXTs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) getTXTRecord(name string, record *BluecatTXTRecord) error {
|
||||||
|
for _, currentRecord := range *g.mockBluecatTXTs {
|
||||||
|
if currentRecord.Name == name {
|
||||||
|
*record = currentRecord
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (res interface{}, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (g mockGatewayClient) deleteTXTRecord(name string) error {
|
||||||
|
*g.mockBluecatTXTs = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g mockGatewayClient) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||||
|
request, _ := http.NewRequest("GET", fmt.Sprintf("%s/users", "http://some.com/api/v1"), nil)
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMockBluecatZone(fqdn string) BluecatZone {
|
||||||
|
props := "absoluteName=" + fqdn
|
||||||
|
return BluecatZone{
|
||||||
|
Properties: props,
|
||||||
|
Name: fqdn,
|
||||||
|
ID: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMockBluecatHostRecord(fqdn, target string) BluecatHostRecord {
|
||||||
|
props := "absoluteName=" + fqdn + "|addresses=" + target + "|"
|
||||||
|
nameParts := strings.Split(fqdn, ".")
|
||||||
|
return BluecatHostRecord{
|
||||||
|
Name: nameParts[0],
|
||||||
|
Properties: props,
|
||||||
|
ID: 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMockBluecatCNAME(alias, target string) BluecatCNAMERecord {
|
||||||
|
props := "absoluteName=" + alias + "|linkedRecordName=" + target + "|"
|
||||||
|
nameParts := strings.Split(alias, ".")
|
||||||
|
return BluecatCNAMERecord{
|
||||||
|
Name: nameParts[0],
|
||||||
|
Properties: props,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMockBluecatTXT(fqdn, txt string) BluecatTXTRecord {
|
||||||
|
return BluecatTXTRecord{
|
||||||
|
Name: fqdn,
|
||||||
|
Text: txt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBluecatProvider(domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun bool, client GatewayClient) *BluecatProvider {
|
||||||
|
return &BluecatProvider{
|
||||||
|
domainFilter: domainFilter,
|
||||||
|
zoneIDFilter: zoneIDFilter,
|
||||||
|
dryRun: dryRun,
|
||||||
|
gatewayClient: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bluecatTestData []struct {
|
||||||
|
TestDescription string
|
||||||
|
Endpoints []*endpoint.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = bluecatTestData{
|
||||||
|
{
|
||||||
|
"first test case", // TODO: better test description
|
||||||
|
[]*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
DNSName: "example.com",
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
Targets: endpoint.Targets{"123.123.123.122"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "nginx.example.com",
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
Targets: endpoint.Targets{"123.123.123.123"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "whitespace.example.com",
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
Targets: endpoint.Targets{"123.123.123.124"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "hack.example.com",
|
||||||
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
|
Targets: endpoint.Targets{"bluecatnetworks.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DNSName: "abc.example.com",
|
||||||
|
RecordType: endpoint.RecordTypeTXT,
|
||||||
|
Targets: endpoint.Targets{"hello"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBluecatRecords(t *testing.T) {
|
||||||
|
client := mockGatewayClient{
|
||||||
|
mockBluecatZones: &[]BluecatZone{
|
||||||
|
createMockBluecatZone("example.com"),
|
||||||
|
},
|
||||||
|
mockBluecatHosts: &[]BluecatHostRecord{
|
||||||
|
createMockBluecatHostRecord("example.com", "123.123.123.122"),
|
||||||
|
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123"),
|
||||||
|
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124"),
|
||||||
|
},
|
||||||
|
mockBluecatCNAMEs: &[]BluecatCNAMERecord{
|
||||||
|
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com"),
|
||||||
|
},
|
||||||
|
mockBluecatTXTs: &[]BluecatTXTRecord{
|
||||||
|
createMockBluecatTXT("abc.example.com", "hello"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := newBluecatProvider(
|
||||||
|
endpoint.NewDomainFilter([]string{"example.com"}),
|
||||||
|
provider.NewZoneIDFilter([]string{""}), false, client)
|
||||||
|
|
||||||
|
for _, ti := range tests {
|
||||||
|
actual, err := provider.Records(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validateEndpoints(t, actual, ti.Endpoints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBluecatApplyChangesCreate(t *testing.T) {
|
||||||
|
client := mockGatewayClient{
|
||||||
|
mockBluecatZones: &[]BluecatZone{
|
||||||
|
createMockBluecatZone("example.com"),
|
||||||
|
},
|
||||||
|
mockBluecatHosts: &[]BluecatHostRecord{},
|
||||||
|
mockBluecatCNAMEs: &[]BluecatCNAMERecord{},
|
||||||
|
mockBluecatTXTs: &[]BluecatTXTRecord{},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := newBluecatProvider(
|
||||||
|
endpoint.NewDomainFilter([]string{"example.com"}),
|
||||||
|
provider.NewZoneIDFilter([]string{""}), false, client)
|
||||||
|
|
||||||
|
for _, ti := range tests {
|
||||||
|
err := provider.ApplyChanges(context.Background(), &plan.Changes{Create: ti.Endpoints})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := provider.Records(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validateEndpoints(t, actual, []*endpoint.Endpoint{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestBluecatApplyChangesDelete(t *testing.T) {
|
||||||
|
client := mockGatewayClient{
|
||||||
|
mockBluecatZones: &[]BluecatZone{
|
||||||
|
createMockBluecatZone("example.com"),
|
||||||
|
},
|
||||||
|
mockBluecatHosts: &[]BluecatHostRecord{
|
||||||
|
createMockBluecatHostRecord("example.com", "123.123.123.122"),
|
||||||
|
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123"),
|
||||||
|
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124"),
|
||||||
|
},
|
||||||
|
mockBluecatCNAMEs: &[]BluecatCNAMERecord{
|
||||||
|
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com"),
|
||||||
|
},
|
||||||
|
mockBluecatTXTs: &[]BluecatTXTRecord{
|
||||||
|
createMockBluecatTXT("abc.example.com", "hello"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := newBluecatProvider(
|
||||||
|
endpoint.NewDomainFilter([]string{"example.com"}),
|
||||||
|
provider.NewZoneIDFilter([]string{""}), false, client)
|
||||||
|
|
||||||
|
for _, ti := range tests {
|
||||||
|
err := provider.ApplyChanges(context.Background(), &plan.Changes{Delete: ti.Endpoints})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := provider.Records(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
validateEndpoints(t, actual, []*endpoint.Endpoint{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: ensure mapChanges method is tested
|
||||||
|
// TODO: ensure findZone method is tested
|
||||||
|
// TODO: ensure zones method is tested
|
||||||
|
// TODO: ensure createRecords method is tested
|
||||||
|
// TODO: ensure deleteRecords method is tested
|
||||||
|
// TODO: ensure recordSet method is tested
|
||||||
|
|
||||||
|
// TODO: Figure out why recordSet.res is not being set properly
|
||||||
|
func TestBluecatRecordset(t *testing.T) {
|
||||||
|
client := mockGatewayClient{
|
||||||
|
mockBluecatZones: &[]BluecatZone{
|
||||||
|
createMockBluecatZone("example.com"),
|
||||||
|
},
|
||||||
|
mockBluecatHosts: &[]BluecatHostRecord{
|
||||||
|
createMockBluecatHostRecord("example.com", "123.123.123.122"),
|
||||||
|
createMockBluecatHostRecord("nginx.example.com", "123.123.123.123"),
|
||||||
|
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124"),
|
||||||
|
},
|
||||||
|
mockBluecatCNAMEs: &[]BluecatCNAMERecord{
|
||||||
|
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com"),
|
||||||
|
},
|
||||||
|
mockBluecatTXTs: &[]BluecatTXTRecord{
|
||||||
|
createMockBluecatTXT("abc.example.com", "hello"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
provider := newBluecatProvider(
|
||||||
|
endpoint.NewDomainFilter([]string{"example.com"}),
|
||||||
|
provider.NewZoneIDFilter([]string{""}), false, client)
|
||||||
|
|
||||||
|
// Test txt records for recordSet function
|
||||||
|
testTxtEndpoint := endpoint.NewEndpoint("abc.example.com", endpoint.RecordTypeTXT, "hello")
|
||||||
|
txtObj := bluecatCreateTXTRecordRequest{
|
||||||
|
AbsoluteName: testTxtEndpoint.DNSName,
|
||||||
|
Text: testTxtEndpoint.Targets[0],
|
||||||
|
}
|
||||||
|
txtRecords := []BluecatTXTRecord{
|
||||||
|
createMockBluecatTXT("abc.example.com", "hello"),
|
||||||
|
}
|
||||||
|
expected := bluecatRecordSet{
|
||||||
|
obj: &txtObj,
|
||||||
|
res: &txtRecords,
|
||||||
|
}
|
||||||
|
actual, err := provider.recordSet(testTxtEndpoint, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, actual.obj, expected.obj)
|
||||||
|
assert.Equal(t, actual.res, expected.res)
|
||||||
|
|
||||||
|
// Test a records for recordSet function
|
||||||
|
testHostEndpoint := endpoint.NewEndpoint("whitespace.example.com", endpoint.RecordTypeA, "123.123.123.124")
|
||||||
|
hostObj := bluecatCreateHostRecordRequest{
|
||||||
|
AbsoluteName: testHostEndpoint.DNSName,
|
||||||
|
IP4Address: testHostEndpoint.Targets[0],
|
||||||
|
}
|
||||||
|
hostRecords := []BluecatHostRecord{
|
||||||
|
createMockBluecatHostRecord("whitespace.example.com", "123.123.123.124"),
|
||||||
|
}
|
||||||
|
hostExpected := bluecatRecordSet{
|
||||||
|
obj: &hostObj,
|
||||||
|
res: &hostRecords,
|
||||||
|
}
|
||||||
|
hostActual, err := provider.recordSet(testHostEndpoint, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, hostActual.obj, hostExpected.obj)
|
||||||
|
assert.Equal(t, hostActual.res, hostExpected.res)
|
||||||
|
|
||||||
|
// Test CName records for recordSet function
|
||||||
|
testCnameEndpoint := endpoint.NewEndpoint("hack.example.com", endpoint.RecordTypeCNAME, "bluecatnetworks.com")
|
||||||
|
cnameObj := bluecatCreateCNAMERecordRequest{
|
||||||
|
AbsoluteName: testCnameEndpoint.DNSName,
|
||||||
|
LinkedRecord: testCnameEndpoint.Targets[0],
|
||||||
|
}
|
||||||
|
cnameRecords := []BluecatCNAMERecord{
|
||||||
|
createMockBluecatCNAME("hack.example.com", "bluecatnetworks.com"),
|
||||||
|
}
|
||||||
|
cnameExpected := bluecatRecordSet{
|
||||||
|
obj: &cnameObj,
|
||||||
|
res: &cnameRecords,
|
||||||
|
}
|
||||||
|
cnameActual, err := provider.recordSet(testCnameEndpoint, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, cnameActual.obj, cnameExpected.obj)
|
||||||
|
assert.Equal(t, cnameActual.res, cnameExpected.res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) {
|
||||||
|
assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected)
|
||||||
|
}
|
@ -91,6 +91,10 @@ func NewRfc2136Provider(host string, port int, zoneName string, insecure bool, k
|
|||||||
return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg)
|
return nil, errors.Errorf("%s is not supported TSIG algorithm", secretAlg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if krb5Realm == "" {
|
||||||
|
krb5Realm = strings.ToUpper(zoneName)
|
||||||
|
}
|
||||||
|
|
||||||
r := &rfc2136Provider{
|
r := &rfc2136Provider{
|
||||||
nameserver: net.JoinHostPort(host, strconv.Itoa(port)),
|
nameserver: net.JoinHostPort(host, strconv.Itoa(port)),
|
||||||
zoneName: dns.Fqdn(zoneName),
|
zoneName: dns.Fqdn(zoneName),
|
||||||
|
200
source/gloo.go
Normal file
200
source/gloo.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020n The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
proxyGVR = schema.GroupVersionResource{
|
||||||
|
Group: "gloo.solo.io",
|
||||||
|
Version: "v1",
|
||||||
|
Resource: "proxies",
|
||||||
|
}
|
||||||
|
virtualServiceGVR = schema.GroupVersionResource{
|
||||||
|
Group: "gateway.solo.io",
|
||||||
|
Version: "v1",
|
||||||
|
Resource: "virtualservices",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Basic redefinition of "Proxy" CRD : https://github.com/solo-io/gloo/blob/v1.4.6/projects/gloo/pkg/api/v1/proxy.pb.go
|
||||||
|
type proxy struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
Metadata metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
Spec proxySpec `json:"spec,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxySpec struct {
|
||||||
|
Listeners []proxySpecListener `json:"listeners,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxySpecListener struct {
|
||||||
|
HTTPListener proxySpecHTTPListener `json:"httpListener,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxySpecHTTPListener struct {
|
||||||
|
VirtualHosts []proxyVirtualHost `json:"virtualHosts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxyVirtualHost struct {
|
||||||
|
Domains []string `json:"domains,omitempty"`
|
||||||
|
Metadata proxyVirtualHostMetadata `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxyVirtualHostMetadata struct {
|
||||||
|
Source []proxyVirtualHostMetadataSource `json:"sources,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type proxyVirtualHostMetadataSource struct {
|
||||||
|
Kind string `json:"kind,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type glooSource struct {
|
||||||
|
dynamicKubeClient dynamic.Interface
|
||||||
|
kubeClient kubernetes.Interface
|
||||||
|
glooNamespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGlooSource creates a new glooSource with the given config
|
||||||
|
func NewGlooSource(dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, glooNamespace string) (Source, error) {
|
||||||
|
return &glooSource{
|
||||||
|
dynamicKubeClient,
|
||||||
|
kubeClient,
|
||||||
|
glooNamespace,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *glooSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoints returns endpoint objects
|
||||||
|
func (gs *glooSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||||
|
endpoints := []*endpoint.Endpoint{}
|
||||||
|
|
||||||
|
proxies, err := gs.dynamicKubeClient.Resource(proxyGVR).Namespace(gs.glooNamespace).List(ctx, metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, obj := range proxies.Items {
|
||||||
|
proxy := proxy{}
|
||||||
|
jsonString, err := obj.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(jsonString, &proxy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Gloo: Find %s proxy", proxy.Metadata.Name)
|
||||||
|
proxyTargets, err := gs.proxyTargets(ctx, proxy.Metadata.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Gloo[%s]: Find %d target(s) (%+v)", proxy.Metadata.Name, len(proxyTargets), proxyTargets)
|
||||||
|
proxyEndpoints, err := gs.generateEndpointsFromProxy(ctx, &proxy, proxyTargets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("Gloo[%s]: Generate %d endpoint(s)", proxy.Metadata.Name, len(proxyEndpoints))
|
||||||
|
endpoints = append(endpoints, proxyEndpoints...)
|
||||||
|
}
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *glooSource) generateEndpointsFromProxy(ctx context.Context, proxy *proxy, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
|
||||||
|
endpoints := []*endpoint.Endpoint{}
|
||||||
|
for _, listener := range proxy.Spec.Listeners {
|
||||||
|
for _, virtualHost := range listener.HTTPListener.VirtualHosts {
|
||||||
|
annotations, err := gs.annotationsFromProxySource(ctx, virtualHost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ttl, err := getTTLFromAnnotations(annotations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annotations)
|
||||||
|
for _, domain := range virtualHost.Domains {
|
||||||
|
endpoints = append(endpoints, endpointsForHostname(strings.TrimSuffix(domain, "."), targets, ttl, providerSpecific, setIdentifier)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *glooSource) annotationsFromProxySource(ctx context.Context, virtualHost proxyVirtualHost) (map[string]string, error) {
|
||||||
|
annotations := map[string]string{}
|
||||||
|
for _, src := range virtualHost.Metadata.Source {
|
||||||
|
kind := sourceKind(src.Kind)
|
||||||
|
if kind != nil {
|
||||||
|
source, err := gs.dynamicKubeClient.Resource(*kind).Namespace(src.Namespace).Get(ctx, src.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for key, value := range source.GetAnnotations() {
|
||||||
|
annotations[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annotations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *glooSource) proxyTargets(ctx context.Context, name string) (endpoint.Targets, error) {
|
||||||
|
svc, err := gs.kubeClient.CoreV1().Services(gs.glooNamespace).Get(ctx, name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets endpoint.Targets
|
||||||
|
switch svc.Spec.Type {
|
||||||
|
case corev1.ServiceTypeLoadBalancer:
|
||||||
|
for _, lb := range svc.Status.LoadBalancer.Ingress {
|
||||||
|
if lb.IP != "" {
|
||||||
|
targets = append(targets, lb.IP)
|
||||||
|
}
|
||||||
|
if lb.Hostname != "" {
|
||||||
|
targets = append(targets, lb.Hostname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.WithField("gateway", name).WithField("service", svc).Warn("Gloo: Proxy service type not supported")
|
||||||
|
}
|
||||||
|
return targets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sourceKind(kind string) *schema.GroupVersionResource {
|
||||||
|
switch kind {
|
||||||
|
case "*v1.VirtualService":
|
||||||
|
return &virtualServiceGVR
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
320
source/gloo_test.go
Normal file
320
source/gloo_test.go
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020n The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
fakeDynamic "k8s.io/client-go/dynamic/fake"
|
||||||
|
fakeKube "k8s.io/client-go/kubernetes/fake"
|
||||||
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time validation that glooSource is a Source.
|
||||||
|
var _ Source = &glooSource{}
|
||||||
|
|
||||||
|
const defaultGlooNamespace = "gloo-system"
|
||||||
|
|
||||||
|
// Internal proxy test
|
||||||
|
var internalProxy = proxy{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: proxyGVR.GroupVersion().String(),
|
||||||
|
Kind: "Proxy",
|
||||||
|
},
|
||||||
|
Metadata: metav1.ObjectMeta{
|
||||||
|
Name: "internal",
|
||||||
|
Namespace: defaultGlooNamespace,
|
||||||
|
},
|
||||||
|
Spec: proxySpec{
|
||||||
|
Listeners: []proxySpecListener{
|
||||||
|
{
|
||||||
|
HTTPListener: proxySpecHTTPListener{
|
||||||
|
VirtualHosts: []proxyVirtualHost{
|
||||||
|
{
|
||||||
|
Domains: []string{"a.test", "b.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.Unknown",
|
||||||
|
Name: "my-unknown-svc",
|
||||||
|
Namespace: "unknown",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"c.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.VirtualService",
|
||||||
|
Name: "my-internal-svc",
|
||||||
|
Namespace: "internal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var internalProxySvc = corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: internalProxy.Metadata.Name,
|
||||||
|
Namespace: internalProxy.Metadata.Namespace,
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
},
|
||||||
|
Status: corev1.ServiceStatus{
|
||||||
|
LoadBalancer: corev1.LoadBalancerStatus{
|
||||||
|
Ingress: []corev1.LoadBalancerIngress{
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
IP: "203.0.113.1",
|
||||||
|
},
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
IP: "203.0.113.2",
|
||||||
|
},
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
IP: "203.0.113.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var internalProxySource = metav1.PartialObjectMetadata{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: virtualServiceGVR.GroupVersion().String(),
|
||||||
|
Kind: "VirtualService",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: internalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Name,
|
||||||
|
Namespace: internalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Namespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"external-dns.alpha.kubernetes.io/ttl": "42",
|
||||||
|
"external-dns.alpha.kubernetes.io/aws-geolocation-country-code": "LU",
|
||||||
|
"external-dns.alpha.kubernetes.io/set-identifier": "identifier",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// External proxy test
|
||||||
|
var externalProxy = proxy{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: proxyGVR.GroupVersion().String(),
|
||||||
|
Kind: "Proxy",
|
||||||
|
},
|
||||||
|
Metadata: metav1.ObjectMeta{
|
||||||
|
Name: "external",
|
||||||
|
Namespace: defaultGlooNamespace,
|
||||||
|
},
|
||||||
|
Spec: proxySpec{
|
||||||
|
Listeners: []proxySpecListener{
|
||||||
|
{
|
||||||
|
HTTPListener: proxySpecHTTPListener{
|
||||||
|
VirtualHosts: []proxyVirtualHost{
|
||||||
|
{
|
||||||
|
Domains: []string{"d.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.Unknown",
|
||||||
|
Name: "my-unknown-svc",
|
||||||
|
Namespace: "unknown",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Domains: []string{"e.test"},
|
||||||
|
Metadata: proxyVirtualHostMetadata{
|
||||||
|
Source: []proxyVirtualHostMetadataSource{
|
||||||
|
{
|
||||||
|
Kind: "*v1.VirtualService",
|
||||||
|
Name: "my-external-svc",
|
||||||
|
Namespace: "external",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalProxySvc = corev1.Service{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: externalProxy.Metadata.Name,
|
||||||
|
Namespace: externalProxy.Metadata.Namespace,
|
||||||
|
},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
Type: corev1.ServiceTypeLoadBalancer,
|
||||||
|
},
|
||||||
|
Status: corev1.ServiceStatus{
|
||||||
|
LoadBalancer: corev1.LoadBalancerStatus{
|
||||||
|
Ingress: []corev1.LoadBalancerIngress{
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
Hostname: "a.example.org",
|
||||||
|
},
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
Hostname: "b.example.org",
|
||||||
|
},
|
||||||
|
corev1.LoadBalancerIngress{
|
||||||
|
Hostname: "c.example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalProxySource = metav1.PartialObjectMetadata{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: virtualServiceGVR.GroupVersion().String(),
|
||||||
|
Kind: "VirtualService",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: externalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Name,
|
||||||
|
Namespace: externalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Namespace,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"external-dns.alpha.kubernetes.io/ttl": "24",
|
||||||
|
"external-dns.alpha.kubernetes.io/aws-geolocation-country-code": "JP",
|
||||||
|
"external-dns.alpha.kubernetes.io/set-identifier": "identifier-external",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlooSource(t *testing.T) {
|
||||||
|
fakeKubernetesClient := fakeKube.NewSimpleClientset()
|
||||||
|
fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
|
||||||
|
|
||||||
|
source, err := NewGlooSource(fakeDynamicClient, fakeKubernetesClient, defaultGlooNamespace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, source)
|
||||||
|
|
||||||
|
internalProxyUnstructured := unstructured.Unstructured{}
|
||||||
|
externalProxyUnstructured := unstructured.Unstructured{}
|
||||||
|
|
||||||
|
internalProxySourceUnstructured := unstructured.Unstructured{}
|
||||||
|
externalProxySourceUnstructured := unstructured.Unstructured{}
|
||||||
|
|
||||||
|
internalProxyAsJSON, err := json.Marshal(internalProxy)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
externalProxyAsJSON, err := json.Marshal(externalProxy)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
internalProxySvcAsJSON, err := json.Marshal(internalProxySource)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
externalProxySvcAsJSON, err := json.Marshal(externalProxySource)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NoError(t, internalProxyUnstructured.UnmarshalJSON(internalProxyAsJSON))
|
||||||
|
assert.NoError(t, externalProxyUnstructured.UnmarshalJSON(externalProxyAsJSON))
|
||||||
|
|
||||||
|
assert.NoError(t, internalProxySourceUnstructured.UnmarshalJSON(internalProxySvcAsJSON))
|
||||||
|
assert.NoError(t, externalProxySourceUnstructured.UnmarshalJSON(externalProxySvcAsJSON))
|
||||||
|
|
||||||
|
// Create proxy resources
|
||||||
|
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &internalProxyUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &externalProxyUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Create proxy source
|
||||||
|
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(internalProxySource.Namespace).Create(context.Background(), &internalProxySourceUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(externalProxySource.Namespace).Create(context.Background(), &externalProxySourceUnstructured, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Create proxy service resources
|
||||||
|
_, err = fakeKubernetesClient.CoreV1().Services(internalProxySvc.GetNamespace()).Create(context.Background(), &internalProxySvc, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = fakeKubernetesClient.CoreV1().Services(externalProxySvc.GetNamespace()).Create(context.Background(), &externalProxySvc, metav1.CreateOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
endpoints, err := source.Endpoints(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, endpoints, 5)
|
||||||
|
assert.Equal(t, endpoints, []*endpoint.Endpoint{
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "a.test",
|
||||||
|
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: 0,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "b.test",
|
||||||
|
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
RecordTTL: 0,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "c.test",
|
||||||
|
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||||
|
RecordType: endpoint.RecordTypeA,
|
||||||
|
SetIdentifier: "identifier",
|
||||||
|
RecordTTL: 42,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
endpoint.ProviderSpecificProperty{
|
||||||
|
Name: "aws/geolocation-country-code",
|
||||||
|
Value: "LU",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "d.test",
|
||||||
|
Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname},
|
||||||
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
|
RecordTTL: 0,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||||
|
},
|
||||||
|
&endpoint.Endpoint{
|
||||||
|
DNSName: "e.test",
|
||||||
|
Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname},
|
||||||
|
RecordType: endpoint.RecordTypeCNAME,
|
||||||
|
SetIdentifier: "identifier-external",
|
||||||
|
RecordTTL: 24,
|
||||||
|
Labels: endpoint.Labels{},
|
||||||
|
ProviderSpecific: endpoint.ProviderSpecific{
|
||||||
|
endpoint.ProviderSpecificProperty{
|
||||||
|
Name: "aws/geolocation-country-code",
|
||||||
|
Value: "JP",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -61,6 +61,7 @@ type Config struct {
|
|||||||
CFUsername string
|
CFUsername string
|
||||||
CFPassword string
|
CFPassword string
|
||||||
ContourLoadBalancerService string
|
ContourLoadBalancerService string
|
||||||
|
GlooNamespace string
|
||||||
SkipperRouteGroupVersion string
|
SkipperRouteGroupVersion string
|
||||||
RequestTimeout time.Duration
|
RequestTimeout time.Duration
|
||||||
}
|
}
|
||||||
@ -239,6 +240,16 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewContourHTTPProxySource(dynamicClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
return NewContourHTTPProxySource(dynamicClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
||||||
|
case "gloo-proxy":
|
||||||
|
kubernetesClient, err := p.KubeClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dynamicClient, err := p.DynamicKubernetesClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewGlooSource(dynamicClient, kubernetesClient, cfg.GlooNamespace)
|
||||||
case "openshift-route":
|
case "openshift-route":
|
||||||
ocpClient, err := p.OpenShiftClient()
|
ocpClient, err := p.OpenShiftClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user