mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Added new provider: Akamai FastDNS
This commit is contained in:
parent
4be51a9445
commit
3d821d74ce
2
go.mod
2
go.mod
@ -9,6 +9,7 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/adal v0.6.0
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.0.0-00010101000000-000000000000
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.5
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||
github.com/alecthomas/colour v0.1.0 // indirect
|
||||
github.com/alecthomas/kingpin v2.2.5+incompatible
|
||||
@ -64,6 +65,7 @@ replace (
|
||||
github.com/Azure/go-autorest/autorest/adal => github.com/Azure/go-autorest/autorest/adal v0.6.0
|
||||
github.com/Azure/go-autorest/autorest/azure/auth => github.com/Azure/go-autorest/autorest/azure/auth v0.3.0
|
||||
github.com/golang/glog => github.com/kubermatic/glog-logrus v0.0.0-20180829085450-3fa5b9870d1d
|
||||
github.com/h2non/gock => gopkg.in/h2non/gock.v1 v1.0.14
|
||||
istio.io/api => istio.io/api v0.0.0-20190820204432-483f2547d882
|
||||
istio.io/istio => istio.io/istio v0.0.0-20190911205955-c2bd59595ce6
|
||||
k8s.io/api => k8s.io/api v0.0.0-20190817221950-ebce17126a01
|
||||
|
12
go.sum
12
go.sum
@ -60,6 +60,8 @@ github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPE
|
||||
github.com/SermoDigital/jose v0.9.1/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.5 h1:6/ofsc2djpS+bsyOG10cDJI1ftigCiulAfIZpxSua6k=
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.5/go.mod h1:L3YOfsK9Dzvjxj1/Q5OG58SDCGGOwtzgi734Kpdc03c=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||
github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
|
||||
@ -192,6 +194,8 @@ github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-ini/ini v1.33.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ini/ini v1.44.0 h1:8+SRbfpRFlIunpSum4BEf1ClTtVjOgKzgBv9pHFkI6w=
|
||||
github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
@ -292,6 +296,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20171214222146-0e7658f8ee99/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/consul v1.3.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
@ -413,6 +418,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/nesv/go-dynect v0.6.0 h1:Ow/DiSm4LAISwnFku/FITSQHnU6pBvhQMsUE5Gu6Oq4=
|
||||
github.com/nesv/go-dynect v0.6.0/go.mod h1:GHRBRKzTwjAMhosHJQq/KrZaFkXIFyJ5zRE7thGXXrs=
|
||||
github.com/nic-at/rc0go v1.1.0 h1:k6/Bru/npTjmCSFw65ulYRw/b3ycIS30t6/YM4r42V4=
|
||||
@ -549,6 +555,9 @@ github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A
|
||||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92 h1:Q76MzqJu++vAfhj0mVf7t0F4xHUbg+V/d/Uk5PBQjRU=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92/go.mod h1:AZuEfReFWdvtU0LatbLpo70t3lqdLvph2D5mqFP0bkA=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
@ -726,9 +735,12 @@ gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn+
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/logfmt.v0 v0.3.0/go.mod h1:mRLMcMLrml5h2Ux/H+4zccFOlVCiRvOvndsolsJoU8Q=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
|
12
main.go
12
main.go
@ -111,6 +111,18 @@ func main() {
|
||||
|
||||
var p provider.Provider
|
||||
switch cfg.Provider {
|
||||
case "akamai":
|
||||
p = provider.NewAkamaiProvider(
|
||||
provider.AkamaiConfig{
|
||||
DomainFilter: domainFilter,
|
||||
ZoneIDFilter: zoneIDFilter,
|
||||
ServiceConsumerDomain: cfg.AkamaiServiceConsumerDomain,
|
||||
ClientToken: cfg.AkamaiClientToken,
|
||||
ClientSecret: cfg.AkamaiClientSecret,
|
||||
AccessToken: cfg.AkamaiAccessToken,
|
||||
DryRun: cfg.DryRun,
|
||||
},
|
||||
)
|
||||
case "alibabacloud":
|
||||
p, err = provider.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
|
||||
case "aws":
|
||||
|
@ -77,6 +77,10 @@ type Config struct {
|
||||
CloudflareZonesPerPage int
|
||||
CoreDNSPrefix string
|
||||
RcodezeroTXTEncrypt bool
|
||||
AkamaiServiceConsumerDomain string
|
||||
AkamaiClientToken string
|
||||
AkamaiClientSecret string
|
||||
AkamaiAccessToken string
|
||||
InfobloxGridHost string
|
||||
InfobloxWapiPort int
|
||||
InfobloxWapiUsername string
|
||||
@ -169,6 +173,10 @@ var defaultConfig = &Config{
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
RcodezeroTXTEncrypt: false,
|
||||
AkamaiServiceConsumerDomain: "",
|
||||
AkamaiClientToken: "",
|
||||
AkamaiClientSecret: "",
|
||||
AkamaiAccessToken: "",
|
||||
InfobloxGridHost: "",
|
||||
InfobloxWapiPort: 443,
|
||||
InfobloxWapiUsername: "admin",
|
||||
@ -289,7 +297,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("service-type-filter", "The service types to take care about (default: all, expected: ClusterIP, NodePort, LoadBalancer or ExternalName)").StringsVar(&cfg.ServiceTypeFilter)
|
||||
|
||||
// Flags related to providers
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
|
||||
app.Flag("provider", "The DNS provider where the DNS records will be created (required, options: aws, aws-sd, google, azure, azure-dns, azure-private-dns, cloudflare, rcodezero, digitalocean, dnsimple, akamai, infoblox, dyn, designate, coredns, skydns, inmemory, pdns, oci, exoscale, linode, rfc2136, ns1, transip, vinyldns, rdns)").Required().PlaceHolder("provider").EnumVar(&cfg.Provider, "aws", "aws-sd", "google", "azure", "azure-dns", "azure-private-dns", "alibabacloud", "cloudflare", "rcodezero", "digitalocean", "dnsimple", "akamai", "infoblox", "dyn", "designate", "coredns", "skydns", "inmemory", "pdns", "oci", "exoscale", "linode", "rfc2136", "ns1", "transip", "vinyldns", "rdns")
|
||||
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("zone-id-filter", "Filter target zones by hosted zone id; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.ZoneIDFilter)
|
||||
@ -313,6 +321,10 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
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("coredns-prefix", "When using the CoreDNS provider, specify the prefix name").Default(defaultConfig.CoreDNSPrefix).StringVar(&cfg.CoreDNSPrefix)
|
||||
app.Flag("akamai-serviceconsumerdomain", "When using the Akamai provider, specify the base URL (required when --provider=akamai)").Default(defaultConfig.AkamaiServiceConsumerDomain).StringVar(&cfg.AkamaiServiceConsumerDomain)
|
||||
app.Flag("akamai-client-token", "When using the Akamai provider, specify the client token (required when --provider=akamai)").Default(defaultConfig.AkamaiClientToken).StringVar(&cfg.AkamaiClientToken)
|
||||
app.Flag("akamai-client-secret", "When using the Akamai provider, specify the client secret (required when --provider=akamai)").Default(defaultConfig.AkamaiClientSecret).StringVar(&cfg.AkamaiClientSecret)
|
||||
app.Flag("akamai-access-token", "When using the Akamai provider, specify the access token (required when --provider=akamai)").Default(defaultConfig.AkamaiAccessToken).StringVar(&cfg.AkamaiAccessToken)
|
||||
app.Flag("infoblox-grid-host", "When using the Infoblox provider, specify the Grid Manager host (required when --provider=infoblox)").Default(defaultConfig.InfobloxGridHost).StringVar(&cfg.InfobloxGridHost)
|
||||
app.Flag("infoblox-wapi-port", "When using the Infoblox provider, specify the WAPI port (default: 443)").Default(strconv.Itoa(defaultConfig.InfobloxWapiPort)).IntVar(&cfg.InfobloxWapiPort)
|
||||
app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername)
|
||||
|
@ -59,6 +59,10 @@ var (
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
AkamaiServiceConsumerDomain: "",
|
||||
AkamaiClientToken: "",
|
||||
AkamaiClientSecret: "",
|
||||
AkamaiAccessToken: "",
|
||||
InfobloxGridHost: "",
|
||||
InfobloxWapiPort: 443,
|
||||
InfobloxWapiUsername: "admin",
|
||||
@ -125,6 +129,10 @@ var (
|
||||
CloudflareProxied: true,
|
||||
CloudflareZonesPerPage: 20,
|
||||
CoreDNSPrefix: "/coredns/",
|
||||
AkamaiServiceConsumerDomain: "oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
|
||||
AkamaiClientToken: "o184671d5307a388180fbf7f11dbdf46",
|
||||
AkamaiClientSecret: "o184671d5307a388180fbf7f11dbdf46",
|
||||
AkamaiAccessToken: "o184671d5307a388180fbf7f11dbdf46",
|
||||
InfobloxGridHost: "127.0.0.1",
|
||||
InfobloxWapiPort: 8443,
|
||||
InfobloxWapiUsername: "infoblox",
|
||||
@ -206,6 +214,10 @@ func TestParseFlags(t *testing.T) {
|
||||
"--cloudflare-proxied",
|
||||
"--cloudflare-zones-per-page=20",
|
||||
"--coredns-prefix=/coredns/",
|
||||
"--akamai-serviceconsumerdomain=oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
|
||||
"--akamai-client-token=o184671d5307a388180fbf7f11dbdf46",
|
||||
"--akamai-client-secret=o184671d5307a388180fbf7f11dbdf46",
|
||||
"--akamai-access-token=o184671d5307a388180fbf7f11dbdf46",
|
||||
"--infoblox-grid-host=127.0.0.1",
|
||||
"--infoblox-wapi-port=8443",
|
||||
"--infoblox-wapi-username=infoblox",
|
||||
@ -286,6 +298,10 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_CLOUDFLARE_PROXIED": "1",
|
||||
"EXTERNAL_DNS_CLOUDFLARE_ZONES_PER_PAGE": "20",
|
||||
"EXTERNAL_DNS_COREDNS_PREFIX": "/coredns/",
|
||||
"EXTERNAL_DNS_AKAMAI_SERVICECONSUMERDOMAIN": "oooo-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net",
|
||||
"EXTERNAL_DNS_AKAMAI_CLIENT_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
||||
"EXTERNAL_DNS_AKAMAI_CLIENT_SECRET": "o184671d5307a388180fbf7f11dbdf46",
|
||||
"EXTERNAL_DNS_AKAMAI_ACCESS_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
||||
"EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
|
||||
"EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
|
||||
|
@ -43,6 +43,22 @@ func ValidateConfig(cfg *externaldns.Config) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Akamai provider specific validations
|
||||
if cfg.Provider == "akamai" {
|
||||
if cfg.AkamaiServiceConsumerDomain == "" {
|
||||
return errors.New("no Akamai ServiceConsumerDomain specified")
|
||||
}
|
||||
if cfg.AkamaiClientToken == "" {
|
||||
return errors.New("no Akamai client token specified")
|
||||
}
|
||||
if cfg.AkamaiClientSecret == "" {
|
||||
return errors.New("no Akamai client secret specified")
|
||||
}
|
||||
if cfg.AkamaiAccessToken == "" {
|
||||
return errors.New("no Akamai access token specified")
|
||||
}
|
||||
}
|
||||
|
||||
// Infoblox provider specific validations
|
||||
if cfg.Provider == "infoblox" {
|
||||
if cfg.InfobloxGridHost == "" {
|
||||
|
414
provider/akamai.go
Normal file
414
provider/akamai.go
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
Copyright 2017 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 provider
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
c "github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type akamaiClient interface {
|
||||
NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*http.Request, error)
|
||||
Do(config edgegrid.Config, req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type akamaiOpenClient struct{}
|
||||
|
||||
func (*akamaiOpenClient) NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*http.Request, error) {
|
||||
return c.NewRequest(config, method, path, body)
|
||||
}
|
||||
|
||||
func (*akamaiOpenClient) Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {
|
||||
return c.Do(config, req)
|
||||
}
|
||||
|
||||
// AkamaiConfig clarifies the method signature
|
||||
type AkamaiConfig struct {
|
||||
DomainFilter DomainFilter
|
||||
ZoneIDFilter ZoneIDFilter
|
||||
ServiceConsumerDomain string
|
||||
ClientToken string
|
||||
ClientSecret string
|
||||
AccessToken string
|
||||
DryRun bool
|
||||
}
|
||||
|
||||
// AkamaiProvider implements the DNS provider for Akamai.
|
||||
type AkamaiProvider struct {
|
||||
domainFilter DomainFilter
|
||||
zoneIDFilter ZoneIDFilter
|
||||
config edgegrid.Config
|
||||
dryRun bool
|
||||
client akamaiClient
|
||||
}
|
||||
|
||||
type akamaiError struct {
|
||||
Title string `json:"title"`
|
||||
Status int `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
RequestID string `json:"requestId"`
|
||||
}
|
||||
|
||||
type akamaiZones struct {
|
||||
Zones []akamaiZone `json:"zones"`
|
||||
}
|
||||
|
||||
type akamaiZone struct {
|
||||
ContractID string `json:"contractId"`
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
type akamaiRecordsets struct {
|
||||
Recordsets []akamaiRecord `json:"recordsets"`
|
||||
}
|
||||
|
||||
type akamaiRecord struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
TTL int64 `json:"ttl"`
|
||||
Rdata []interface{} `json:"rdata"`
|
||||
}
|
||||
|
||||
// NewAkamaiProvider initializes a new Akamai DNS based Provider.
|
||||
func NewAkamaiProvider(akamaiConfig AkamaiConfig) *AkamaiProvider {
|
||||
edgeGridConfig := edgegrid.Config{
|
||||
Host: akamaiConfig.ServiceConsumerDomain,
|
||||
ClientToken: akamaiConfig.ClientToken,
|
||||
ClientSecret: akamaiConfig.ClientSecret,
|
||||
AccessToken: akamaiConfig.AccessToken,
|
||||
MaxBody: 1024,
|
||||
HeaderToSign: []string{
|
||||
"X-External-DNS",
|
||||
},
|
||||
Debug: false,
|
||||
}
|
||||
|
||||
provider := &AkamaiProvider{
|
||||
domainFilter: akamaiConfig.DomainFilter,
|
||||
zoneIDFilter: akamaiConfig.ZoneIDFilter,
|
||||
config: edgeGridConfig,
|
||||
dryRun: akamaiConfig.DryRun,
|
||||
client: &akamaiOpenClient{},
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) request(method, path string, body io.Reader) ([]byte, error) {
|
||||
req, err := p.client.NewRequest(p.config, method, fmt.Sprintf("https://%s/%s", p.config.Host, path), body)
|
||||
if err != nil {
|
||||
log.Errorf("Akamai client failed to prepare the request")
|
||||
return nil, err
|
||||
}
|
||||
resp, err := p.client.Do(p.config, req)
|
||||
if err != nil {
|
||||
log.Errorf("Akamai client failed to do the request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//204 means no body on success by choice
|
||||
if resp.StatusCode == 204 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
byt, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Errorf("ioutil failed to read the response-body of the request to Akamai")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//catch authentication errors here, they aren't handled very well by the client
|
||||
akamaiError := akamaiError{}
|
||||
err = json.Unmarshal([]byte(byt), &akamaiError)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to unmarshal the response-body of the request to Akamai")
|
||||
return nil, err
|
||||
}
|
||||
if akamaiError.Status >= 300 {
|
||||
log.Errorf("Received an error from Akamai!\ntitle: %s \nstatus: %v \ndetail: %s \nrequestId: %s", akamaiError.Title, akamaiError.Status, akamaiError.Detail, akamaiError.RequestID)
|
||||
return nil, errors.New("AkamaiError")
|
||||
}
|
||||
|
||||
return byt, err
|
||||
}
|
||||
|
||||
//Look here for endpoint documentation -> https://developer.akamai.com/api/web_performance/fast_dns_zone_management/v2.html#getzones
|
||||
func (p *AkamaiProvider) fetchZones() (zones akamaiZones, err error) {
|
||||
log.Debugf("Trying to fetch zones from Akamai")
|
||||
res, err := p.request("GET", "config-dns/v2/zones?showAll=true&types=primary%2Csecondary", nil)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to fetch zones from Akamai")
|
||||
return zones, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(res), &zones)
|
||||
if err != nil {
|
||||
log.Errorf("Could not unmarshal json response from Akamai on zone request")
|
||||
return zones, err
|
||||
}
|
||||
|
||||
filteredZones := akamaiZones{}
|
||||
for _, zone := range zones.Zones {
|
||||
if !p.zoneIDFilter.Match(zone.ContractID) {
|
||||
log.Debugf("Skipping zone: '%s' with ZoneID: '%s', it does not match against ZoneID filters", zone.Zone, zone.ContractID)
|
||||
continue
|
||||
}
|
||||
filteredZones.Zones = append(filteredZones.Zones, akamaiZone{ContractID: zone.ContractID, Zone: zone.Zone})
|
||||
log.Debugf("Fetched zone: '%s' (ZoneID: %s)", zone.Zone, zone.ContractID)
|
||||
}
|
||||
lenFilteredZones := len(filteredZones.Zones)
|
||||
if lenFilteredZones == 0 {
|
||||
log.Warnf("No zones could be fetched")
|
||||
} else {
|
||||
log.Debugf("Fetched '%d' zones from Akamai", lenFilteredZones)
|
||||
}
|
||||
|
||||
return filteredZones, nil
|
||||
}
|
||||
|
||||
//Look here for endpoint documentation -> https://developer.akamai.com/api/web_performance/fast_dns_zone_management/v2.html#getzonerecordsets
|
||||
func (p *AkamaiProvider) fetchRecordSet(zone string) (recordSet akamaiRecordsets, err error) {
|
||||
log.Debugf("Trying to fetch endpoints for zone: '%s' from Akamai", zone)
|
||||
res, err := p.request("GET", "config-dns/v2/zones/"+zone+"/recordsets?showAll=true&types=A%2CTXT%2CCNAME", nil)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to fetch records from Akamai for zone: '%s'", zone)
|
||||
return recordSet, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(res), &recordSet)
|
||||
if err != nil {
|
||||
log.Errorf("Could not unmarshal json response from Akamai for zone: '%s' on request", zone)
|
||||
return recordSet, err
|
||||
}
|
||||
|
||||
return recordSet, nil
|
||||
}
|
||||
|
||||
//Records returns the list of records in a given zone.
|
||||
func (p *AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint, err error) {
|
||||
zones, err := p.fetchZones()
|
||||
if err != nil {
|
||||
log.Warnf("No zones to fetch endpoints from!")
|
||||
return endpoints, err
|
||||
}
|
||||
for _, zone := range zones.Zones {
|
||||
records, _ := p.fetchRecordSet(zone.Zone)
|
||||
for _, record := range records.Recordsets {
|
||||
rdata := make([]string, len(record.Rdata))
|
||||
|
||||
for i, v := range record.Rdata {
|
||||
rdata[i] = v.(string)
|
||||
}
|
||||
|
||||
if !p.domainFilter.Match(record.Name) {
|
||||
log.Debugf("Skipping endpoint DNSName: '%s' RecordType: '%s', it does not match against Domain filters", record.Name, record.Type)
|
||||
continue
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(record.Name, record.Type, rdata...))
|
||||
log.Debugf("Fetched endpoint DNSName: '%s' RecordType: '%s' Rdata: '%s')", record.Name, record.Type, rdata)
|
||||
}
|
||||
}
|
||||
lenEndpoints := len(endpoints)
|
||||
if lenEndpoints == 0 {
|
||||
log.Warnf("No endpoints could be fetched")
|
||||
} else {
|
||||
log.Debugf("Fetched '%d' endpoints from Akamai", lenEndpoints)
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// ApplyChanges applies a given set of changes in a given zone.
|
||||
func (p *AkamaiProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
zoneNameIDMapper := zoneIDName{}
|
||||
zones, err := p.fetchZones()
|
||||
if err != nil {
|
||||
log.Warnf("No zones to fetch endpoints from!")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, z := range zones.Zones {
|
||||
zoneNameIDMapper[z.Zone] = z.Zone
|
||||
}
|
||||
|
||||
_, cf := p.createRecords(zoneNameIDMapper, changes.Create)
|
||||
if !p.dryRun {
|
||||
if len(cf) > 0 {
|
||||
log.Warnf("Not all desired endpoints could be created, retrying next iteration")
|
||||
for _, f := range cf {
|
||||
log.Warnf("Not created was DNSName: '%s' RecordType: '%s'", f.DNSName, f.RecordType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, df := p.deleteRecords(zoneNameIDMapper, changes.Delete)
|
||||
if !p.dryRun {
|
||||
if len(df) > 0 {
|
||||
log.Warnf("Not all endpoints that require deletion could be deleted, retrying next iteration")
|
||||
for _, f := range df {
|
||||
log.Warnf("Not deleted was DNSName: '%s' RecordType: '%s'", f.DNSName, f.RecordType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, uf := p.updateNewRecords(zoneNameIDMapper, changes.UpdateNew)
|
||||
if !p.dryRun {
|
||||
if len(uf) > 0 {
|
||||
log.Warnf("Not all endpoints that require updating could be updated, retrying next iteration")
|
||||
for _, f := range uf {
|
||||
log.Warnf("Not updated was DNSName: '%s' RecordType: '%s'", f.DNSName, f.RecordType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, uold := range changes.UpdateOld {
|
||||
if !p.dryRun {
|
||||
log.Debugf("UpdateOld (ignored) for DNSName: '%s' RecordType: '%s'", uold.DNSName, uold.RecordType)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) newAkamaiRecord(dnsName, recordType string, targets ...string) *akamaiRecord {
|
||||
cleanTargets := make([]interface{}, len(targets))
|
||||
for idx, target := range targets {
|
||||
cleanTargets[idx] = strings.TrimSuffix(target, ".")
|
||||
}
|
||||
return &akamaiRecord{
|
||||
Name: strings.TrimSuffix(dnsName, "."),
|
||||
Rdata: cleanTargets,
|
||||
Type: recordType,
|
||||
TTL: 300,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) newAkamaiRecordsets(dnsName, recordType string, targets ...string) *akamaiRecordsets {
|
||||
akamaiRecords := make([]akamaiRecord, 0)
|
||||
akamaiRecord := p.newAkamaiRecord(dnsName, recordType, targets...)
|
||||
akamaiRecords = append(akamaiRecords, *akamaiRecord)
|
||||
|
||||
return &akamaiRecordsets{
|
||||
Recordsets: akamaiRecords,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) createRecords(zoneNameIDMapper zoneIDName, endpoints []*endpoint.Endpoint) (created []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
||||
log.Debugf("Skipping creation at Akamai of endpoint DNSName: '%s' RecordType: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
||||
continue
|
||||
}
|
||||
if zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName); zoneName != "" {
|
||||
akamaiRecord := p.newAkamaiRecord(endpoint.DNSName, endpoint.RecordType, endpoint.Targets...)
|
||||
body, _ := json.MarshalIndent(akamaiRecord, "", " ")
|
||||
|
||||
log.Infof("Create new Endpoint at Akamai FastDNS - Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
||||
|
||||
if p.dryRun {
|
||||
continue
|
||||
}
|
||||
_, err := p.request("POST", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create Akamai endpoint DNSName: '%s' RecordType: '%s' for zone: '%s'", endpoint.DNSName, endpoint.RecordType, zoneName)
|
||||
failed = append(failed, endpoint)
|
||||
continue
|
||||
}
|
||||
created = append(created, endpoint)
|
||||
} else {
|
||||
log.Warnf("No matching zone for endpoint addition DNSName: '%s' RecordType: '%s'", endpoint.DNSName, endpoint.RecordType)
|
||||
failed = append(failed, endpoint)
|
||||
}
|
||||
}
|
||||
return created, failed
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) deleteRecords(zoneNameIDMapper zoneIDName, endpoints []*endpoint.Endpoint) (deleted []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
||||
log.Debugf("Skipping deletion at Akamai of endpoint: '%s' type: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
||||
continue
|
||||
}
|
||||
if zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName); zoneName != "" {
|
||||
log.Infof("Deletion at Akamai FastDNS - Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
||||
|
||||
if p.dryRun {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := p.request("DELETE", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, nil)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to delete Akamai endpoint DNSName: '%s' for zone: '%s'", endpoint.DNSName, zoneName)
|
||||
failed = append(failed, endpoint)
|
||||
continue
|
||||
}
|
||||
deleted = append(deleted, endpoint)
|
||||
} else {
|
||||
log.Warnf("No matching zone for endpoint deletion DNSName: '%s' RecordType: '%s'", endpoint.DNSName, endpoint.RecordType)
|
||||
failed = append(failed, endpoint)
|
||||
}
|
||||
}
|
||||
return deleted, failed
|
||||
}
|
||||
|
||||
func (p *AkamaiProvider) updateNewRecords(zoneNameIDMapper zoneIDName, endpoints []*endpoint.Endpoint) (updated []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
||||
log.Debugf("Skipping update at Akamai of endpoint DNSName: '%s' RecordType: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
||||
continue
|
||||
}
|
||||
if zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName); zoneName != "" {
|
||||
akamaiRecord := p.newAkamaiRecord(endpoint.DNSName, endpoint.RecordType, endpoint.Targets...)
|
||||
body, _ := json.MarshalIndent(akamaiRecord, "", " ")
|
||||
|
||||
log.Infof("Updating endpoint at Akamai FastDNS - Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
||||
|
||||
if p.dryRun {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := p.request("PUT", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
log.Errorf("Failed to update Akamai endpoint DNSName: '%s' for zone: '%s'", endpoint.DNSName, zoneName)
|
||||
failed = append(failed, endpoint)
|
||||
continue
|
||||
}
|
||||
updated = append(updated, endpoint)
|
||||
} else {
|
||||
log.Warnf("No matching zone for endpoint update DNSName: '%s' RecordType: '%s'", endpoint.DNSName, endpoint.RecordType)
|
||||
failed = append(failed, endpoint)
|
||||
}
|
||||
}
|
||||
return updated, failed
|
||||
}
|
336
provider/akamai_test.go
Normal file
336
provider/akamai_test.go
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
Copyright 2017 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 provider
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockAkamaiClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *mockAkamaiClient) NewRequest(config edgegrid.Config, met, p string, b io.Reader) (*http.Request, error) {
|
||||
switch {
|
||||
case met == "GET":
|
||||
switch {
|
||||
case strings.HasPrefix(p, "https:///config-dns/v2/zones?"):
|
||||
b = bytes.NewReader([]byte("{\"zones\":[{\"contractId\":\"Test\",\"zone\":\"example.com\"},{\"contractId\":\"Exclude-Me\",\"zone\":\"exclude.me\"}]}"))
|
||||
case strings.HasPrefix(p, "https:///config-dns/v2/zones/example.com/"):
|
||||
b = bytes.NewReader([]byte("{\"recordsets\":[{\"name\":\"www.example.com\",\"type\":\"A\",\"ttl\":300,\"rdata\":[\"10.0.0.2\",\"10.0.0.3\"]},{\"name\":\"www.example.com\",\"type\":\"TXT\",\"ttl\":300,\"rdata\":[\"heritage=external-dns,external-dns/owner=default\"]}]}"))
|
||||
case strings.HasPrefix(p, "https:///config-dns/v2/zones/exclude.me/"):
|
||||
b = bytes.NewReader([]byte("{\"recordsets\":[{\"name\":\"www.exclude.me\",\"type\":\"A\",\"ttl\":300,\"rdata\":[\"192.168.0.1\",\"192.168.0.2\"]}]}"))
|
||||
}
|
||||
case met == "DELETE":
|
||||
b = bytes.NewReader([]byte("{\"title\": \"Success\", \"status\": 200, \"detail\": \"Record deleted\", \"requestId\": \"4321\"}"))
|
||||
}
|
||||
req := httptest.NewRequest(met, p, b)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (m *mockAkamaiClient) Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := ioutil.ReadAll(r.Body)
|
||||
io.WriteString(w, string(b))
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func TestRequestError(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
m := "GET"
|
||||
p := ""
|
||||
b := "{\"title\": \"MockError\", \"status\": 404, \"detail\": \"MockError\", \"requestId\": \"1234\"}"
|
||||
x, _ := c.request(m, p, bytes.NewReader([]byte(b)))
|
||||
assert.Nil(t, x)
|
||||
}
|
||||
|
||||
func TestFetchZonesZoneIDFilter(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
ZoneIDFilter: NewZoneIDFilter([]string{"Test"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
x, _ := c.fetchZones()
|
||||
y, _ := json.Marshal(x)
|
||||
if assert.NotNil(t, y) {
|
||||
assert.Equal(t, "{\"zones\":[{\"contractId\":\"Test\",\"zone\":\"example.com\"}]}", string(y))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchZonesEmpty(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
DomainFilter: NewDomainFilter([]string{"Nonexistent"}),
|
||||
ZoneIDFilter: NewZoneIDFilter([]string{"Nonexistent"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
x, _ := c.fetchZones()
|
||||
y, _ := json.Marshal(x)
|
||||
if assert.NotNil(t, y) {
|
||||
assert.Equal(t, "{\"zones\":null}", string(y))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchRecordset1(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
x, _ := c.fetchRecordSet("example.com")
|
||||
y, _ := json.Marshal(x)
|
||||
if assert.NotNil(t, y) {
|
||||
assert.Equal(t, "{\"recordsets\":[{\"name\":\"www.example.com\",\"type\":\"A\",\"ttl\":300,\"rdata\":[\"10.0.0.2\",\"10.0.0.3\"]},{\"name\":\"www.example.com\",\"type\":\"TXT\",\"ttl\":300,\"rdata\":[\"heritage=external-dns,external-dns/owner=default\"]}]}", string(y))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchRecordset2(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
x, _ := c.fetchRecordSet("exclude.me")
|
||||
y, _ := json.Marshal(x)
|
||||
if assert.NotNil(t, y) {
|
||||
assert.Equal(t, "{\"recordsets\":[{\"name\":\"www.exclude.me\",\"type\":\"A\",\"ttl\":300,\"rdata\":[\"192.168.0.1\",\"192.168.0.2\"]}]}", string(y))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAkamaiRecords(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "192.168.0.1", "192.168.0.2"))
|
||||
|
||||
x, _ := c.Records(context.Background())
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAkamaiRecordsEmpty(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
ZoneIDFilter: NewZoneIDFilter([]string{"Nonexistent"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
x, _ := c.Records(context.Background())
|
||||
assert.Nil(t, x)
|
||||
}
|
||||
|
||||
func TestAkamaiRecordsFilters(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
DomainFilter: NewDomainFilter([]string{"www.exclude.me"}),
|
||||
ZoneIDFilter: NewZoneIDFilter([]string{"Exclude-Me"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "192.168.0.1", "192.168.0.2"))
|
||||
|
||||
x, _ := c.Records(context.Background())
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRecords(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
|
||||
x, _ := c.createRecords(zoneNameIDMapper, endpoints)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateRecordsDomainFilter(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
DomainFilter: NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
|
||||
x, _ := c.createRecords(zoneNameIDMapper, exclude)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRecords(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
|
||||
x, _ := c.deleteRecords(zoneNameIDMapper, endpoints)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteRecordsDomainFilter(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
DomainFilter: NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
|
||||
x, _ := c.deleteRecords(zoneNameIDMapper, exclude)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRecords(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
|
||||
x, _ := c.updateNewRecords(zoneNameIDMapper, endpoints)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRecordsDomainFilter(t *testing.T) {
|
||||
config := AkamaiConfig{
|
||||
DomainFilter: NewDomainFilter([]string{"example.com"}),
|
||||
}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
zoneNameIDMapper := zoneIDName{"example.com": "example.com"}
|
||||
endpoints := make([]*endpoint.Endpoint, 0)
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||
|
||||
x, _ := c.updateNewRecords(zoneNameIDMapper, exclude)
|
||||
if assert.NotNil(t, x) {
|
||||
assert.Equal(t, endpoints, x)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAkamaiApplyChanges(t *testing.T) {
|
||||
config := AkamaiConfig{}
|
||||
|
||||
client := &mockAkamaiClient{}
|
||||
c := NewAkamaiProvider(config)
|
||||
c.client = client
|
||||
|
||||
changes := &plan.Changes{}
|
||||
changes.Create = []*endpoint.Endpoint{
|
||||
{DNSName: "www.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}, RecordTTL: 300},
|
||||
{DNSName: "test.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}, RecordTTL: 300},
|
||||
{DNSName: "test.this.example.com", RecordType: "A", Targets: endpoint.Targets{"127.0.0.1"}, RecordTTL: 300},
|
||||
{DNSName: "www.example.com", RecordType: "TXT", Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default"}, RecordTTL: 300},
|
||||
{DNSName: "test.example.com", RecordType: "TXT", Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default"}, RecordTTL: 300},
|
||||
{DNSName: "test.this.example.com", RecordType: "TXT", Targets: endpoint.Targets{"heritage=external-dns,external-dns/owner=default"}, RecordTTL: 300},
|
||||
{DNSName: "another.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}},
|
||||
}
|
||||
changes.Delete = []*endpoint.Endpoint{{DNSName: "delete.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}, RecordTTL: 300}}
|
||||
changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "old.example.com", RecordType: "A", Targets: endpoint.Targets{"target-old"}, RecordTTL: 300}}
|
||||
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "update.example.com", Targets: endpoint.Targets{"target-new"}, RecordType: "CNAME", RecordTTL: 300}}
|
||||
apply := c.ApplyChanges(context.Background(), changes)
|
||||
assert.Nil(t, apply)
|
||||
}
|
Loading…
Reference in New Issue
Block a user